Skip to content

Commit 6ee9d4c

Browse files
authored
blob: add ListIterator.All, a Go 1.23 iterator (#3608)
1 parent 8eb1938 commit 6ee9d4c

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
lines changed

blob/blob.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import (
7070
"fmt"
7171
"hash"
7272
"io"
73+
"iter"
7374
"log"
7475
"mime"
7576
"net/http"
@@ -611,6 +612,28 @@ func (i *ListIterator) Next(ctx context.Context) (*ListObject, error) {
611612
return i.Next(ctx)
612613
}
613614

615+
// All iterates over the iterator, returning a *ListObject and a download function for each entry.
616+
func (i *ListIterator) All(ctx context.Context, err *error) iter.Seq2[*ListObject, func(io.Writer, *ReaderOptions) error] {
617+
return func(yield func(*ListObject, func(io.Writer, *ReaderOptions) error) bool) {
618+
for {
619+
obj, itErr := i.Next(ctx)
620+
if itErr == io.EOF {
621+
return
622+
}
623+
if itErr != nil {
624+
*err = itErr
625+
return
626+
}
627+
downloadFunc := func(w io.Writer, opts *ReaderOptions) error {
628+
return i.b.Download(ctx, obj.Key, w, opts)
629+
}
630+
if !yield(obj, downloadFunc) {
631+
return
632+
}
633+
}
634+
}
635+
}
636+
614637
// ListObject represents a single blob returned from List.
615638
type ListObject struct {
616639
// Key is the key for this blob.

blob/blob_iter_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package blob_test
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"fmt"
7+
"testing"
8+
9+
"github.com/google/go-cmp/cmp"
10+
"gocloud.dev/blob/memblob"
11+
)
12+
13+
// Verify ListIterator.All.
14+
func TestListIterator_All(t *testing.T) {
15+
ctx := context.Background()
16+
b := memblob.OpenBucket(nil)
17+
defer b.Close()
18+
19+
// Initialize the bucket with some keys.
20+
want := map[string]string{}
21+
for _, key := range []string{"a", "b", "c"} {
22+
contents := fmt.Sprintf("%s-contents", key)
23+
if err := b.WriteAll(ctx, key, []byte(contents), nil); err != nil {
24+
t.Fatalf("failed to initialize key %q: %v", key, err)
25+
}
26+
want[key] = contents
27+
}
28+
29+
// Iterate over the bucket using iter.All.
30+
var err error
31+
got := map[string]string{}
32+
iter := b.List(nil)
33+
for obj, download := range iter.All(ctx, &err) {
34+
var buf bytes.Buffer
35+
if dErr := download(&buf, nil); dErr != nil {
36+
t.Errorf("failed to download %q: %v", obj.Key, dErr)
37+
}
38+
got[obj.Key] = string(buf.Bytes())
39+
}
40+
if err != nil {
41+
t.Fatalf("iteration failed: %v", err)
42+
}
43+
if diff := cmp.Diff(got, want); diff != "" {
44+
t.Errorf("got %v, want %v, diff %s", got, want, diff)
45+
}
46+
}

blob/example_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package blob_test
1616

1717
import (
18+
"bytes"
1819
"context"
1920
"fmt"
2021
"io"
@@ -219,12 +220,29 @@ func ExampleBucket_List() {
219220
fmt.Println(obj.Key)
220221
}
221222

223+
// Alternatively, use All to iterate (and optionally download):
224+
fmt.Println()
225+
fmt.Println("Now, using an iterator:")
226+
iter = bucket.List(nil)
227+
for obj, download := range iter.All(ctx, &err) {
228+
var buf bytes.Buffer
229+
_ = download(&buf, nil) // ignore error and use default ReaderOptions
230+
fmt.Printf("%s: %s\n", obj.Key, string(buf.Bytes()))
231+
}
232+
222233
// Output:
223234
// foo0.txt
224235
// foo1.txt
225236
// foo2.txt
226237
// foo3.txt
227238
// foo4.txt
239+
//
240+
// Now, using an iterator:
241+
// foo0.txt: Go Cloud Development Kit
242+
// foo1.txt: Go Cloud Development Kit
243+
// foo2.txt: Go Cloud Development Kit
244+
// foo3.txt: Go Cloud Development Kit
245+
// foo4.txt: Go Cloud Development Kit
228246
}
229247

230248
func ExampleBucket_List_withDelimiter() {

0 commit comments

Comments
 (0)