Open4
aws-sdk-go-v2のページングがあるAPIをGo 1.23のイテレーターで実装する
例えば ecs.ListTasks をイテレーターにするにはこんな感じのコードになる。
func ecsNewListTasksIterator(ctx context.Context, c *ecs.Client, in *ecs.ListTasksInput) func(func(*ecs.ListTasksOutput, error) bool) {
return func(yield func(*ecs.ListTasksOutput, error) bool) {
for {
out, err := c.ListTasks(ctx, in)
if err != nil {
yield(nil, err)
return
}
if !yield(out, err) || out.NextToken == nil {
return
}
in.NextToken = out.NextToken
}
}
}
使う側はこう
client := ecs.NewFromConfig(awsCfg)
for out, err := range ecsNewListTasksIterator(ctx, client, input) {
if err != nil {
return err
}
// ... out を使って何かする
}
Genericsを使って一般化するとこう
func newAnyIterator[IN any, OUT any, OPT any](ctx context.Context, do func(context.Context, *IN, ...OPT) (*OUT, error), next func(*IN, *OUT) bool, in *IN, opt ...OPT) func(func(*OUT, error) bool) {
return func(yield func(*OUT, error) bool) {
for {
out, err := do(ctx, in, opt...)
if err != nil {
yield(nil, err)
return
}
if !yield(out, err) {
return
}
if !next(in, out) { // if next returns true, has a next page. continue
return
}
}
}
}
-
do
= aws-sdk-go-v2 のあらゆる client のメソッドとおなじ interface の関数 -
next
= output から次のページがあるかを判定し、あればinputにnextTokenを設定してtrue
を返す関数- ここはAPIごとにtokenの名前が違ったりするので個別に書くしかない
- あるいはreflectで頑張るか…?
でこれを使うとこう書ける
// next の実装
func ecsListTasksNext(in *ecs.ListTasksInput, out *ecs.ListTasksOutput) bool {
if out.NextToken == nil {
return false
}
in.NextToken = out.NextToken
return true
}
for res, err := range newAnyIterator(ctx, client.ListTasks, ecsListTasksNext, input) {
// ...
(参考)
もともと sdk v2 には Paginator という仕組みがあり ecs#ListTasksPaginator
こんな風に書ける。
p := ecs.NewListTasksPaginator(client, input)
for p.HasMorePages() {
out, err := p.NextPage(ctx)
// ...
}
イテレーターにすると、HasMorePagesとNextPageという2つの関数を呼ぶ必要がなくて普通に range でループをまわせばよいのでちょっとだけ分かりやすい。SDK でもイテレータを提供してくれないかな? (v3かなあ…)
イテレーターについてはこちらが分かりやすい