Skip to content

Commit 1fce8cc

Browse files
committed
Add copy and iterator utils
1 parent e8775fe commit 1fce8cc

File tree

6 files changed

+132
-2
lines changed

6 files changed

+132
-2
lines changed

fieldpath/element.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package fieldpath
1818

1919
import (
2020
"fmt"
21+
"iter"
2122
"sort"
2223
"strings"
2324

@@ -156,6 +157,25 @@ func (e PathElement) String() string {
156157
}
157158
}
158159

160+
// Copy returns a copy of the PathElement.
161+
// This is not a full deep copy as any contained value.Value is not copied.
162+
func (e PathElement) Copy() PathElement {
163+
if e.FieldName != nil {
164+
return PathElement{FieldName: e.FieldName}
165+
}
166+
if e.Key != nil {
167+
c := e.Key.Copy()
168+
return PathElement{Key: &c}
169+
}
170+
if e.Value != nil {
171+
return PathElement{Value: e.Value}
172+
}
173+
if e.Index != nil {
174+
return PathElement{Index: e.Index}
175+
}
176+
return e // zero value
177+
}
178+
159179
// KeyByFields is a helper function which constructs a key for an associative
160180
// list type. `nameValues` must have an even number of entries, alternating
161181
// names (type must be string) with values (type must be value.Value). If these
@@ -193,6 +213,16 @@ func (spe sortedPathElements) Len() int { return len(spe) }
193213
func (spe sortedPathElements) Less(i, j int) bool { return spe[i].Less(spe[j]) }
194214
func (spe sortedPathElements) Swap(i, j int) { spe[i], spe[j] = spe[j], spe[i] }
195215

216+
// Copy returns a copy of the PathElementSet.
217+
// This is not a full deep copy as any contained value.Value is not copied.
218+
func (s PathElementSet) Copy() PathElementSet {
219+
out := make(sortedPathElements, len(s.members))
220+
for i := range s.members {
221+
out[i] = s.members[i].Copy()
222+
}
223+
return PathElementSet{members: out}
224+
}
225+
196226
// Insert adds pe to the set.
197227
func (s *PathElementSet) Insert(pe PathElement) {
198228
loc := sort.Search(len(s.members), func(i int) bool {
@@ -315,3 +345,14 @@ func (s *PathElementSet) Iterate(f func(PathElement)) {
315345
f(pe)
316346
}
317347
}
348+
349+
// All iterates over each PathElement in the set. The order is deterministic.
350+
func (s *PathElementSet) All() iter.Seq[PathElement] {
351+
return func(yield func(element PathElement) bool) {
352+
for _, pe := range s.members {
353+
if !yield(pe) {
354+
return
355+
}
356+
}
357+
}
358+
}

fieldpath/element_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ func TestPathElementSet(t *testing.T) {
3333
if !s2.Has(PathElement{}) {
3434
t.Errorf("expected to have something: %#v", s2)
3535
}
36+
c2 := s2.Copy()
37+
if !c2.Equals(s2) {
38+
t.Errorf("expected copy to equal original: %#v, %#v", s2, c2)
39+
}
3640

3741
n1 := "aoeu"
3842
n2 := "asdf"
@@ -60,6 +64,20 @@ func TestPathElementSet(t *testing.T) {
6064
}
6165
i++
6266
})
67+
i = 0
68+
for pe := range s2.All() {
69+
e, a := expected[i], pe.FieldName
70+
if e == nil || a == nil {
71+
if e != a {
72+
t.Errorf("index %v wanted %#v, got %#v", i, e, a)
73+
}
74+
} else {
75+
if *e != *a {
76+
t.Errorf("index %v wanted %#v, got %#v", i, *e, *a)
77+
}
78+
}
79+
i++
80+
}
6381
}
6482

6583
func strptr(s string) *string { return &s }

fieldpath/set.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package fieldpath
1818

1919
import (
2020
"fmt"
21+
"iter"
2122
"sort"
2223
"strings"
2324

@@ -47,6 +48,15 @@ func NewSet(paths ...Path) *Set {
4748
return s
4849
}
4950

51+
// Copy returns a copy of the Set.
52+
// This is not a full deep copy as any contained value.Value is not copied.
53+
func (s *Set) Copy() *Set {
54+
return &Set{
55+
Members: s.Members.Copy(),
56+
Children: s.Children.Copy(),
57+
}
58+
}
59+
5060
// Insert adds the field identified by `p` to the set. Important: parent fields
5161
// are NOT added to the set; if that is desired, they must be added separately.
5262
func (s *Set) Insert(p Path) {
@@ -386,6 +396,15 @@ func (s *Set) Iterate(f func(Path)) {
386396
s.iteratePrefix(Path{}, f)
387397
}
388398

399+
// All iterates over each Path in the set (preorder DFS).
400+
func (s *Set) All() iter.Seq[Path] {
401+
return func(yield func(Path) bool) {
402+
s.Iterate(func(p Path) {
403+
yield(p)
404+
})
405+
}
406+
}
407+
389408
func (s *Set) iteratePrefix(prefix Path, f func(Path)) {
390409
s.Members.Iterate(func(pe PathElement) { f(append(prefix, pe)) })
391410
s.Children.iteratePrefix(prefix, f)
@@ -455,6 +474,16 @@ func (s sortedSetNode) Len() int { return len(s) }
455474
func (s sortedSetNode) Less(i, j int) bool { return s[i].pathElement.Less(s[j].pathElement) }
456475
func (s sortedSetNode) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
457476

477+
// Copy returns a copy of the SetNodeMap.
478+
// This is not a full deep copy as any contained value.Value is not copied.
479+
func (s *SetNodeMap) Copy() SetNodeMap {
480+
out := make(sortedSetNode, len(s.members))
481+
for i, v := range s.members {
482+
out[i] = setNode{pathElement: v.pathElement.Copy(), set: v.set.Copy()}
483+
}
484+
return SetNodeMap{members: out}
485+
}
486+
458487
// Descend adds pe to the set if necessary, returning the associated subset.
459488
func (s *SetNodeMap) Descend(pe PathElement) *Set {
460489
loc := sort.Search(len(s.members), func(i int) bool {
@@ -705,6 +734,15 @@ func (s *SetNodeMap) Iterate(f func(PathElement)) {
705734
}
706735
}
707736

737+
// All iterates over each PathElement in the set.
738+
func (s *SetNodeMap) All() iter.Seq[PathElement] {
739+
return func(yield func(PathElement) bool) {
740+
s.Iterate(func(pe PathElement) {
741+
yield(pe)
742+
})
743+
}
744+
}
745+
708746
func (s *SetNodeMap) iteratePrefix(prefix Path, f func(Path)) {
709747
for _, n := range s.members {
710748
pe := n.pathElement

fieldpath/set_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@ func TestSetIterSize(t *testing.T) {
233233
)
234234

235235
s2 := NewSet()
236-
237236
addedCount := 0
238237
s1.Iterate(func(p Path) {
239238
if s2.Size() != addedCount {
@@ -246,6 +245,19 @@ func TestSetIterSize(t *testing.T) {
246245
addedCount++
247246
})
248247

248+
s2 = NewSet()
249+
addedCount = 0
250+
for p := range s1.All() {
251+
if s2.Size() != addedCount {
252+
t.Errorf("added %v items to set, but size is %v", addedCount, s2.Size())
253+
}
254+
if addedCount > 0 == s2.Empty() {
255+
t.Errorf("added %v items to set, but s2.Empty() is %v", addedCount, s2.Empty())
256+
}
257+
s2.Insert(p)
258+
addedCount++
259+
}
260+
249261
if !s1.Equals(s2) {
250262
// No point in using String() if iterate is broken...
251263
t.Errorf("Iterate missed something?\n%#v\n%#v", s1, s2)
@@ -756,6 +768,19 @@ func TestSetNodeMapIterate(t *testing.T) {
756768
t.Errorf("expected to have iterated over %v, but never did", pe)
757769
}
758770
}
771+
772+
iteratedElements = make(map[string]bool, toAdd)
773+
for pe := range set.All() {
774+
iteratedElements[pe.String()] = true
775+
}
776+
if len(iteratedElements) != toAdd {
777+
t.Errorf("expected %v elements to be iterated over, got %v", toAdd, len(iteratedElements))
778+
}
779+
for _, pe := range addedElements {
780+
if _, ok := iteratedElements[pe]; !ok {
781+
t.Errorf("expected to have iterated over %v, but never did", pe)
782+
}
783+
}
759784
}
760785

761786
func TestFilterByPattern(t *testing.T) {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ require (
1212
github.com/modern-go/reflect2 v1.0.2 // indirect
1313
)
1414

15-
go 1.19
15+
go 1.23

value/fields.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ type Field struct {
3131
// have a different name.
3232
type FieldList []Field
3333

34+
// Copy returns a copy of the FieldList.
35+
// Values are not copied.
36+
func (f FieldList) Copy() FieldList {
37+
c := make(FieldList, len(f))
38+
copy(c, f)
39+
return c
40+
}
41+
3442
// Sort sorts the field list by Name.
3543
func (f FieldList) Sort() {
3644
if len(f) < 2 {

0 commit comments

Comments
 (0)