diff --git a/container/container.go b/container/container.go index ff63adb..f10c320 100644 --- a/container/container.go +++ b/container/container.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "errors" "fmt" + "iter" "strconv" "strings" "time" @@ -337,10 +338,41 @@ func (x Container) Attribute(key string) string { return "" } +// Attributes returns an iterator over all Container attributes. +// +// See also [Container.SetAttribute], [Container.UserAttributes]. +func (x Container) Attributes() iter.Seq2[string, string] { + return func(yield func(string, string) bool) { + attrs := x.v2.GetAttributes() + for i := range attrs { + if !yield(attrs[i].GetKey(), attrs[i].GetValue()) { + return + } + } + } +} + +// Attributes returns an iterator over all non-system Container attributes. +// +// See also [Container.SetAttribute], [Container.Attributes]. +func (x Container) UserAttributes() iter.Seq2[string, string] { + return func(yield func(string, string) bool) { + for key, value := range x.Attributes() { + if !strings.HasPrefix(key, container.SysAttributePrefix) { + if !yield(key, value) { + return + } + } + } + } +} + // IterateAttributes iterates over all Container attributes and passes them // into f. The handler MUST NOT be nil. // // See also SetAttribute, Attribute. +// +// Deprecated: use [Container.Attributes] instead. func (x Container) IterateAttributes(f func(key, val string)) { attrs := x.v2.GetAttributes() for i := range attrs { @@ -352,6 +384,8 @@ func (x Container) IterateAttributes(f func(key, val string)) { // into f. The handler MUST NOT be nil. // // See also SetAttribute, Attribute. +// +// Deprecated: use [Container.UserAttributes] instead. func (x Container) IterateUserAttributes(f func(key, val string)) { attrs := x.v2.GetAttributes() for _, attr := range attrs { diff --git a/container/container_test.go b/container/container_test.go index a66a866..62422ab 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -2,6 +2,7 @@ package container_test import ( "crypto/sha256" + "maps" "strconv" "testing" "time" @@ -159,9 +160,9 @@ func TestContainer_Attribute(t *testing.T) { val.SetAttribute(attrKey2, attrVal2) var i int - val.IterateUserAttributes(func(key, val string) { + for range val.UserAttributes() { i++ - }) + } require.Equal(t, 1, i) var msg v2container.Container @@ -177,11 +178,7 @@ func TestContainer_Attribute(t *testing.T) { require.Equal(t, attrVal1, val2.Attribute(attrKey1)) require.Equal(t, attrVal2, val2.Attribute(attrKey2)) - m := map[string]string{} - - val2.IterateAttributes(func(key, val string) { - m[key] = val - }) + m := maps.Collect(val2.Attributes()) require.GreaterOrEqual(t, len(m), 2) require.Equal(t, attrVal1, m[attrKey1]) diff --git a/container/iterators_test.go b/container/iterators_test.go new file mode 100644 index 0000000..00522a5 --- /dev/null +++ b/container/iterators_test.go @@ -0,0 +1,95 @@ +package container_test + +import ( + "testing" + + containerAPI "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" + "github.com/stretchr/testify/require" +) + +func TestContainer_Attributes(t *testing.T) { + t.Run("empty", func(t *testing.T) { + var n container.Container + t.Run("attributes", func(t *testing.T) { + for range n.Attributes() { + t.Fatalf("handler is called, but it shouldn't") + } + }) + t.Run("user attributes", func(t *testing.T) { + for range n.UserAttributes() { + t.Fatalf("handler is called, but it shouldn't") + } + }) + }) + + var n container.Container + n.SetAttribute(containerAPI.SysAttributeName, "myname") + n.SetAttribute("key1", "value1") + n.SetAttribute("key2", "value2") + n.SetAttribute(containerAPI.SysAttributeZone, "test") + + t.Run("break", func(t *testing.T) { + t.Run("attributes", func(t *testing.T) { + var res [][2]string + for key, value := range n.Attributes() { + if key == "key2" { + break + } + res = append(res, [2]string{key, value}) + } + require.Equal(t, [][2]string{{containerAPI.SysAttributeName, "myname"}, {"key1", "value1"}}, res) + }) + t.Run("user attributes", func(t *testing.T) { + var res [][2]string + for key, value := range n.UserAttributes() { + if key == "key2" { + break + } + res = append(res, [2]string{key, value}) + } + require.Equal(t, [][2]string{{"key1", "value1"}}, res) + }) + }) + t.Run("continue", func(t *testing.T) { + t.Run("attributes", func(t *testing.T) { + var res [][2]string + for key, value := range n.Attributes() { + if key == "key2" { + continue + } + res = append(res, [2]string{key, value}) + } + require.Equal(t, [][2]string{{containerAPI.SysAttributeName, "myname"}, {"key1", "value1"}, {containerAPI.SysAttributeZone, "test"}}, res) + }) + t.Run("user attributes", func(t *testing.T) { + var res [][2]string + for key, value := range n.UserAttributes() { + if key == "key2" { + continue + } + res = append(res, [2]string{key, value}) + } + require.Equal(t, [][2]string{{"key1", "value1"}}, res) + }) + }) + t.Run("attributes", func(t *testing.T) { + var res [][2]string + for key, value := range n.Attributes() { + res = append(res, [2]string{key, value}) + } + require.Equal(t, [][2]string{ + {containerAPI.SysAttributeName, "myname"}, + {"key1", "value1"}, + {"key2", "value2"}, + {containerAPI.SysAttributeZone, "test"}, + }, res) + }) + t.Run("user attributes", func(t *testing.T) { + var res [][2]string + for key, value := range n.UserAttributes() { + res = append(res, [2]string{key, value}) + } + require.Equal(t, [][2]string{{"key1", "value1"}, {"key2", "value2"}}, res) + }) +}