Skip to content

Commit f395ded

Browse files
committed
Apply feedback
1 parent 1311e4d commit f395ded

File tree

3 files changed

+277
-72
lines changed

3 files changed

+277
-72
lines changed

fieldpath/set.go

+101-56
Original file line numberDiff line numberDiff line change
@@ -138,51 +138,51 @@ func (s *Set) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.TypeRef)
138138
}
139139
}
140140

141-
// MustPrefixPattern is the same as PrefixPattern except it panics if parts can't be
142-
// turned into a SetPattern.
143-
func MustPrefixPattern(parts ...interface{}) *SetPattern {
144-
result, err := PrefixPattern(parts...)
141+
// MakePrefixMatcherOrDie is the same as PrefixMatcher except it panics if parts can't be
142+
// turned into a SetMatcher.
143+
func MakePrefixMatcherOrDie(parts ...interface{}) *SetMatcher {
144+
result, err := PrefixMatcher(parts...)
145145
if err != nil {
146146
panic(err)
147147
}
148148
return result
149149
}
150150

151-
// PrefixPattern creates a SetPattern that matches all field paths prefixed by the given list of pattern path parts.
151+
// PrefixMatcher creates a SetMatcher that matches all field paths prefixed by the given list of pattern path parts.
152152
// The pattern parts may any of:
153153
//
154-
// - PathPattern - for wildcards, `MatchAnyPathElement()` can be used as well.
154+
// - PathElementMatcher - for wildcards, `MatchAnyPathElement()` can be used as well.
155155
// - PathElement - for any path element
156156
// - value.FieldList - for associative list keys
157157
// - value.Value - for scalar list elements
158158
// - string - For field names
159159
// - int - for array indices
160-
func PrefixPattern(parts ...interface{}) (*SetPattern, error) {
161-
current := MatchAnySet() // match all field patch suffixes
160+
func PrefixMatcher(parts ...interface{}) (*SetMatcher, error) {
161+
current := MatchAnySet() // match all field path suffixes
162162
for i := len(parts) - 1; i >= 0; i-- {
163163
part := parts[i]
164-
var pattern PathPattern
164+
var pattern PathElementMatcher
165165
switch t := part.(type) {
166-
case PathPattern:
166+
case PathElementMatcher:
167167
pattern = t
168168
case PathElement:
169-
pattern = PathPattern{PathElement: t}
169+
pattern = PathElementMatcher{PathElement: t}
170170
case *value.FieldList:
171171
if len(*t) == 0 {
172172
return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)")
173173
}
174-
pattern = PathPattern{PathElement: PathElement{Key: t}}
174+
pattern = PathElementMatcher{PathElement: PathElement{Key: t}}
175175
case value.Value:
176-
pattern = PathPattern{PathElement: PathElement{Value: &t}}
176+
pattern = PathElementMatcher{PathElement: PathElement{Value: &t}}
177177
case string:
178-
pattern = PathPattern{PathElement: PathElement{FieldName: &t}}
178+
pattern = PathElementMatcher{PathElement: PathElement{FieldName: &t}}
179179
case int:
180-
pattern = PathPattern{PathElement: PathElement{Index: &t}}
180+
pattern = PathElementMatcher{PathElement: PathElement{Index: &t}}
181181
default:
182182
return nil, fmt.Errorf("unexpected type %T", t)
183183
}
184-
current = &SetPattern{
185-
Members: []*MemberSetPattern{{
184+
current = &SetMatcher{
185+
Members: []*SetMemberMatcher{{
186186
Path: pattern,
187187
Child: current,
188188
}},
@@ -191,43 +191,80 @@ func PrefixPattern(parts ...interface{}) (*SetPattern, error) {
191191
return current, nil
192192
}
193193

194-
// MatchAnyPathElement returns a PathPattern that matches any path element.
195-
func MatchAnyPathElement() PathPattern {
196-
return PathPattern{Wildcard: true}
194+
// MatchAnyPathElement returns a PathElementMatcher that matches any path element.
195+
func MatchAnyPathElement() PathElementMatcher {
196+
return PathElementMatcher{Wildcard: true}
197197
}
198198

199-
// MatchAnySet returns a SetPattern that matches any set.
200-
func MatchAnySet() *SetPattern {
201-
return &SetPattern{Wildcard: true}
199+
// MatchAnySet returns a SetMatcher that matches any set.
200+
func MatchAnySet() *SetMatcher {
201+
return &SetMatcher{Wildcard: true}
202202
}
203203

204-
// SetPattern defines a pattern that matches fields in a Set.
205-
// SetPattern is structured much like a Set but with wildcard support.
206-
type SetPattern struct {
204+
// SetMatcher defines a pattern that matches fields in a Set.
205+
// SetMatcher is structured much like a Set but with wildcard support.
206+
type SetMatcher struct {
207207
// Wildcard indicates that all members and children are included in the match.
208208
// If set, the Members field is ignored.
209209
Wildcard bool
210210
// Members provides patterns to match the members of a Set.
211-
Members []*MemberSetPattern
211+
Members []*SetMemberMatcher
212+
}
213+
214+
// Merge merges two SetMatchers into a single SetMatcher that matches the field paths of both SetMatchers.
215+
// During the merge, Members of s2 with the same PathElementMatcher as a member of s are merged into the member of s.
216+
// All other members of s2 are appended to the resulting member list in their original relative order.
217+
// When members are merged, the child SetMatchers are merged by calling this function recursively.
218+
func (s *SetMatcher) Merge(s2 *SetMatcher) *SetMatcher {
219+
if s.Wildcard || s2.Wildcard {
220+
return &SetMatcher{Wildcard: true}
221+
}
222+
223+
// TODO: Optimize. This is O(n^2). In practice, usually a single member is being inserted at a time,
224+
// but that's still O(n). Sorting would help a ton.
225+
var unionedMembers []*SetMemberMatcher
226+
for _, m := range s.Members {
227+
for _, m2 := range s2.Members {
228+
if m.Path.PathElement.Equals(m2.Path.PathElement) && m.Path.Wildcard == m2.Path.Wildcard {
229+
unionedMembers = append(unionedMembers, &SetMemberMatcher{
230+
Path: m.Path,
231+
Child: m.Child.Merge(m2.Child),
232+
})
233+
} else {
234+
unionedMembers = append(unionedMembers, m)
235+
}
236+
}
237+
}
238+
for _, m2 := range s2.Members {
239+
for _, existing := range unionedMembers {
240+
if !m2.Path.PathElement.Equals(existing.Path.PathElement) {
241+
unionedMembers = append(unionedMembers, m2)
242+
}
243+
}
244+
}
245+
246+
return &SetMatcher{
247+
Members: unionedMembers,
248+
}
212249
}
213250

214-
// MemberSetPattern defines a pattern that matches the members of a Set.
215-
// MemberSetPattern is structured much like the elements of a SetNodeMap, but
251+
// SetMemberMatcher defines a pattern that matches the members of a Set.
252+
// SetMemberMatcher is structured much like the elements of a SetNodeMap, but
216253
// with wildcard support.
217-
type MemberSetPattern struct {
254+
type SetMemberMatcher struct {
218255
// Path provides a pattern to match members of a Set.
219256
// If Path is a wildcard, all members of a Set are included in the match.
220257
// Otherwise, if any Path is Equal to a member of a Set, that member is
221258
// included in the match and the children of that member are matched
222259
// against the Child pattern.
223-
Path PathPattern
260+
Path PathElementMatcher
224261

225262
// Child provides a pattern to use for the children of matched members of a Set.
226-
Child *SetPattern
263+
Child *SetMatcher
227264
}
228265

229-
// PathPattern defined a match pattern for a PathElement.
230-
type PathPattern struct {
266+
// PathElementMatcher defined a match pattern for a PathElement.
267+
type PathElementMatcher struct {
231268
// Wildcard indicates that all PathElements are matched by this pattern.
232269
// If set, PathElement is ignored.
233270
Wildcard bool
@@ -237,8 +274,8 @@ type PathPattern struct {
237274
PathElement
238275
}
239276

240-
// FilterByPattern returns a Set with only the field paths that match the pattern.
241-
func (s *Set) FilterByPattern(pattern *SetPattern) *Set {
277+
// FilterIncludeMatches returns a Set with only the field paths that match.
278+
func (s *Set) FilterIncludeMatches(pattern *SetMatcher) *Set {
242279
if pattern.Wildcard {
243280
return s
244281
}
@@ -254,7 +291,7 @@ func (s *Set) FilterByPattern(pattern *SetPattern) *Set {
254291
}
255292
return &Set{
256293
Members: members,
257-
Children: *s.Children.FilterByPattern(pattern),
294+
Children: *s.Children.FilterIncludeMatches(pattern),
258295
}
259296
}
260297

@@ -598,8 +635,8 @@ func (s *SetNodeMap) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.Ty
598635
}
599636
}
600637

601-
// FilterByPattern returns a SetNodeMap with only the field paths that match the pattern.
602-
func (s *SetNodeMap) FilterByPattern(pattern *SetPattern) *SetNodeMap {
638+
// FilterIncludeMatches returns a SetNodeMap with only the field paths that match the pattern.
639+
func (s *SetNodeMap) FilterIncludeMatches(pattern *SetMatcher) *SetNodeMap {
603640
if pattern.Wildcard {
604641
return s
605642
}
@@ -608,7 +645,7 @@ func (s *SetNodeMap) FilterByPattern(pattern *SetPattern) *SetNodeMap {
608645
for _, member := range s.members {
609646
for _, c := range pattern.Members {
610647
if c.Path.Wildcard || c.Path.PathElement.Equals(member.pathElement) {
611-
childSet := member.set.FilterByPattern(c.Child)
648+
childSet := member.set.FilterIncludeMatches(c.Child)
612649
if childSet.Size() > 0 {
613650
out = append(out, setNode{
614651
pathElement: member.pathElement,
@@ -654,23 +691,23 @@ func (s *SetNodeMap) Leaves() *SetNodeMap {
654691
}
655692

656693
// Filter defines an interface for excluding field paths from a set.
657-
// NewExcludeFilter can be used to create a filter that removes
658-
// excluded field paths.
659-
// NewPatternFilter can be used to create a filter that removes all fields except
660-
// the fields that match a field path pattern. PrefixPattern and MustPrefixPattern
694+
// NewExcludeSetFilter can be used to create a filter that removes
695+
// specific field paths and all of their children.
696+
// NewIncludeMatcherFilter can be used to create a filter that removes all fields except
697+
// the fields that match a field path pattern. PrefixMatcher and MakePrefixMatcherOrDie
661698
// can be used to define field path patterns.
662699
type Filter interface {
663700
// Filter returns a filtered copy of the set.
664701
Filter(*Set) *Set
665702
}
666703

667-
// NewExcludeFilter returns a filter that removes field paths in the exclude set.
668-
func NewExcludeFilter(exclude *Set) Filter {
704+
// NewExcludeSetFilter returns a filter that removes field paths in the exclude set.
705+
func NewExcludeSetFilter(exclude *Set) Filter {
669706
return excludeFilter{exclude}
670707
}
671708

672-
// NewExcludeFilterMap converts a map of APIVersion to exclude set to a map of APIVersion to exclude filters.
673-
func NewExcludeFilterMap(resetFields map[APIVersion]*Set) map[APIVersion]Filter {
709+
// NewExcludeFilterSetMap converts a map of APIVersion to exclude set to a map of APIVersion to exclude filters.
710+
func NewExcludeFilterSetMap(resetFields map[APIVersion]*Set) map[APIVersion]Filter {
674711
result := make(map[APIVersion]Filter)
675712
for k, v := range resetFields {
676713
result[k] = excludeFilter{v}
@@ -686,16 +723,24 @@ func (t excludeFilter) Filter(set *Set) *Set {
686723
return set.RecursiveDifference(t.excludeSet)
687724
}
688725

689-
// NewPatternFilter returns a filter that only includes field paths that match the pattern.
690-
// PrefixPattern and MustPrefixPattern can help create basic SetPatterns.
691-
func NewPatternFilter(pattern *SetPattern) Filter {
692-
return patternFilter{pattern}
726+
// NewIncludeMatcherFilter returns a filter that only includes field paths that match the pattern.
727+
// PrefixMatcher and MakePrefixMatcherOrDie can help create basic SetPatterns.
728+
func NewIncludeMatcherFilter(patterns ...*SetMatcher) Filter {
729+
if len(patterns) == 0 {
730+
return includeMatcherFilter{&SetMatcher{Wildcard: true}}
731+
}
732+
pattern := patterns[0]
733+
for i := 1; i < len(patterns); i++ {
734+
pattern = pattern.Merge(patterns[i])
735+
}
736+
737+
return includeMatcherFilter{pattern}
693738
}
694739

695-
type patternFilter struct {
696-
pattern *SetPattern
740+
type includeMatcherFilter struct {
741+
pattern *SetMatcher
697742
}
698743

699-
func (pf patternFilter) Filter(set *Set) *Set {
700-
return set.FilterByPattern(pf.pattern)
744+
func (pf includeMatcherFilter) Filter(set *Set) *Set {
745+
return set.FilterIncludeMatches(pf.pattern)
701746
}

0 commit comments

Comments
 (0)