diff --git a/copy/single.go b/copy/single.go index 6aa4e760..f7aca838 100644 --- a/copy/single.go +++ b/copy/single.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "iter" "maps" "reflect" "slices" @@ -417,7 +418,7 @@ func (ic *imageCopier) compareImageDestinationManifestEqual(ctx context.Context, } } - algos, err := algorithmsByNames(compressionAlgos.Values()) + algos, err := algorithmsByNames(compressionAlgos.All()) if err != nil { return nil, err } @@ -552,7 +553,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) ([]compressiontypes.Algor if srcInfosUpdated || layerDigestsDiffer(srcInfos, destInfos) { ic.manifestUpdates.LayerInfos = destInfos } - algos, err := algorithmsByNames(compressionAlgos.Values()) + algos, err := algorithmsByNames(compressionAlgos.All()) if err != nil { return nil, err } @@ -988,10 +989,10 @@ func computeDiffID(stream io.Reader, decompressor compressiontypes.DecompressorF return digest.Canonical.FromReader(stream) } -// algorithmsByNames returns slice of Algorithms from slice of Algorithm Names -func algorithmsByNames(names []string) ([]compressiontypes.Algorithm, error) { +// algorithmsByNames returns slice of Algorithms from a sequence of Algorithm Names +func algorithmsByNames(names iter.Seq[string]) ([]compressiontypes.Algorithm, error) { result := []compressiontypes.Algorithm{} - for _, name := range names { + for name := range names { algo, err := compression.AlgorithmByName(name) if err != nil { return nil, err diff --git a/internal/set/set.go b/internal/set/set.go index acf30343..6ada730d 100644 --- a/internal/set/set.go +++ b/internal/set/set.go @@ -1,6 +1,10 @@ package set -import "golang.org/x/exp/maps" +import ( + "iter" + + "golang.org/x/exp/maps" +) // FIXME: // - Docstrings @@ -47,6 +51,12 @@ func (s *Set[E]) Empty() bool { return len(s.m) == 0 } -func (s *Set[E]) Values() []E { - return maps.Keys(s.m) +func (s *Set[E]) All() iter.Seq[E] { + return func(yield func(E) bool) { + for _, v := range maps.Keys(s.m) { + if !yield(v) { + return + } + } + } } diff --git a/internal/set/set_test.go b/internal/set/set_test.go index 3e704a95..5c1456dc 100644 --- a/internal/set/set_test.go +++ b/internal/set/set_test.go @@ -1,6 +1,7 @@ package set import ( + "slices" "testing" "github.com/stretchr/testify/assert" @@ -26,7 +27,7 @@ func TestAdd(t *testing.T) { s.Add(2) // Adding an already-present element assert.True(t, s.Contains(2)) // should not contain duplicate value of `2` - assert.ElementsMatch(t, []int{1, 2}, s.Values()) + assert.ElementsMatch(t, []int{1, 2}, slices.Collect(s.All())) // Unrelated elements are unaffected assert.True(t, s.Contains(1)) assert.False(t, s.Contains(3)) @@ -36,7 +37,7 @@ func TestAddSlice(t *testing.T) { s := NewWithValues(1) s.Add(2) s.AddSlice([]int{3, 4}) - assert.ElementsMatch(t, []int{1, 2, 3, 4}, s.Values()) + assert.ElementsMatch(t, []int{1, 2, 3, 4}, slices.Collect(s.All())) } func TestDelete(t *testing.T) { @@ -66,12 +67,20 @@ func TestEmpty(t *testing.T) { assert.True(t, s.Empty()) } -func TestValues(t *testing.T) { +func TestAll(t *testing.T) { s := New[int]() - assert.Empty(t, s.Values()) + assert.Empty(t, slices.Collect(s.All())) s.Add(1) s.Add(2) // ignore duplicate s.Add(2) - assert.ElementsMatch(t, []int{1, 2}, s.Values()) + assert.ElementsMatch(t, []int{1, 2}, slices.Collect(s.All())) + // Break / return inside the range body (yield function returning false) works + var partial []int + for v := range s.All() { + partial = append(partial, v) + break + } + assert.Len(t, partial, 1) + assert.True(t, partial[0] == 1 || partial[0] == 2) } diff --git a/oci/layout/oci_delete.go b/oci/layout/oci_delete.go index f32dd53d..7978229d 100644 --- a/oci/layout/oci_delete.go +++ b/oci/layout/oci_delete.go @@ -123,7 +123,7 @@ func (ref ociReference) getBlobsToDelete(blobsUsedByDescriptorToDelete map[diges // // So, NOTE: the blobPath() call below hard-codes "" even in calls where OCISharedBlobDirPath is set func (ref ociReference) deleteBlobs(blobsToDelete *set.Set[digest.Digest]) error { - for _, digest := range blobsToDelete.Values() { + for digest := range blobsToDelete.All() { blobPath, err := ref.blobPath(digest, "") //Only delete in the local directory, see comment above if err != nil { return err diff --git a/pkg/blobinfocache/memory/memory.go b/pkg/blobinfocache/memory/memory.go index 9d4125d6..8e513d41 100644 --- a/pkg/blobinfocache/memory/memory.go +++ b/pkg/blobinfocache/memory/memory.go @@ -240,7 +240,7 @@ func (mem *cache) candidateLocations(transport types.ImageTransport, scope types if uncompressedDigest = mem.uncompressedDigestLocked(primaryDigest); uncompressedDigest != "" { otherDigests := mem.digestsByUncompressed[uncompressedDigest] // nil if not present in the map if otherDigests != nil { - for _, d := range otherDigests.Values() { + for d := range otherDigests.All() { if d != primaryDigest && d != uncompressedDigest { res = mem.appendReplacementCandidates(res, transport, scope, d, v2Options) } diff --git a/pkg/docker/config/config.go b/pkg/docker/config/config.go index da2238a0..65993715 100644 --- a/pkg/docker/config/config.go +++ b/pkg/docker/config/config.go @@ -124,7 +124,7 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon // Now use `GetCredentials` to the specific auth configs for each // previously listed registry. allCreds := make(map[string]types.DockerAuthConfig) - for _, key := range allKeys.Values() { + for key := range allKeys.All() { creds, err := GetCredentials(sys, key) if err != nil { // Note: we rely on the logging in `GetCredentials`.