@@ -17,6 +17,8 @@ limitations under the License.
17
17
package fieldpath
18
18
19
19
import (
20
+ "fmt"
21
+ "sigs.k8s.io/structured-merge-diff/v4/value"
20
22
"sort"
21
23
"strings"
22
24
@@ -136,6 +138,198 @@ func (s *Set) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.TypeRef)
136
138
}
137
139
}
138
140
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 ... )
145
+ if err != nil {
146
+ panic (err )
147
+ }
148
+ return result
149
+ }
150
+
151
+ // PrefixMatcher creates a SetMatcher that matches all field paths prefixed by the given list of matcher path parts.
152
+ // The matcher parts may any of:
153
+ //
154
+ // - PathElementMatcher - for wildcards, `MatchAnyPathElement()` can be used as well.
155
+ // - PathElement - for any path element
156
+ // - value.FieldList - for listMap keys
157
+ // - value.Value - for scalar list elements
158
+ // - string - For field names
159
+ // - int - for array indices
160
+ func PrefixMatcher (parts ... interface {}) (* SetMatcher , error ) {
161
+ current := MatchAnySet () // match all field path suffixes
162
+ for i := len (parts ) - 1 ; i >= 0 ; i -- {
163
+ part := parts [i ]
164
+ var pattern PathElementMatcher
165
+ switch t := part .(type ) {
166
+ case PathElementMatcher :
167
+ // any path matcher, including wildcard
168
+ pattern = t
169
+ case PathElement :
170
+ // any path element
171
+ pattern = PathElementMatcher {PathElement : t }
172
+ case * value.FieldList :
173
+ // a listMap key
174
+ if len (* t ) == 0 {
175
+ return nil , fmt .Errorf ("associative list key type path elements must have at least one key (got zero)" )
176
+ }
177
+ pattern = PathElementMatcher {PathElement : PathElement {Key : t }}
178
+ case value.Value :
179
+ // a scalar or set-type list element
180
+ pattern = PathElementMatcher {PathElement : PathElement {Value : & t }}
181
+ case string :
182
+ // a plain field name
183
+ pattern = PathElementMatcher {PathElement : PathElement {FieldName : & t }}
184
+ case int :
185
+ // a plain list index
186
+ pattern = PathElementMatcher {PathElement : PathElement {Index : & t }}
187
+ default :
188
+ return nil , fmt .Errorf ("unexpected type %T" , t )
189
+ }
190
+ current = & SetMatcher {
191
+ members : []* SetMemberMatcher {{
192
+ Path : pattern ,
193
+ Child : current ,
194
+ }},
195
+ }
196
+ }
197
+ return current , nil
198
+ }
199
+
200
+ // MatchAnyPathElement returns a PathElementMatcher that matches any path element.
201
+ func MatchAnyPathElement () PathElementMatcher {
202
+ return PathElementMatcher {Wildcard : true }
203
+ }
204
+
205
+ // MatchAnySet returns a SetMatcher that matches any set.
206
+ func MatchAnySet () * SetMatcher {
207
+ return & SetMatcher {wildcard : true }
208
+ }
209
+
210
+ // NewSetMatcher returns a new SetMatcher.
211
+ // Wildcard members take precedent over non-wildcard members;
212
+ // all non-wildcard members are ignored if there is a wildcard members.
213
+ func NewSetMatcher (wildcard bool , members ... * SetMemberMatcher ) * SetMatcher {
214
+ sort .Sort (sortedMemberMatcher (members ))
215
+ return & SetMatcher {wildcard : wildcard , members : members }
216
+ }
217
+
218
+ // SetMatcher defines a matcher that matches fields in a Set.
219
+ // SetMatcher is structured much like a Set but with wildcard support.
220
+ type SetMatcher struct {
221
+ // wildcard indicates that all members and children are included in the match.
222
+ // If set, the members field is ignored.
223
+ wildcard bool
224
+ // members provides patterns to match the members of a Set.
225
+ // Wildcard members are sorted before non-wildcards and take precedent over
226
+ // non-wildcard members.
227
+ members sortedMemberMatcher
228
+ }
229
+
230
+ type sortedMemberMatcher []* SetMemberMatcher
231
+
232
+ func (s sortedMemberMatcher ) Len () int { return len (s ) }
233
+ func (s sortedMemberMatcher ) Less (i , j int ) bool { return s [i ].Path .Less (s [j ].Path ) }
234
+ func (s sortedMemberMatcher ) Swap (i , j int ) { s [i ], s [j ] = s [j ], s [i ] }
235
+ func (s sortedMemberMatcher ) Find (p PathElementMatcher ) (location int , ok bool ) {
236
+ return sort .Find (len (s ), func (i int ) int {
237
+ return s [i ].Path .Compare (p )
238
+ })
239
+ }
240
+
241
+ // Merge merges s and s2 and returns a SetMatcher that matches all field paths matched by either s or s2.
242
+ // During the merge, members of s and s2 with the same PathElementMatcher merged into a single member
243
+ // with the children of each merged by calling this function recursively.
244
+ func (s * SetMatcher ) Merge (s2 * SetMatcher ) * SetMatcher {
245
+ if s .wildcard || s2 .wildcard {
246
+ return NewSetMatcher (true )
247
+ }
248
+ merged := make (sortedMemberMatcher , len (s .members ), len (s .members )+ len (s2 .members ))
249
+ copy (merged , s .members )
250
+ for _ , m := range s2 .members {
251
+ if i , ok := s .members .Find (m .Path ); ok {
252
+ // since merged is a shallow copy, do not modify elements in place
253
+ merged [i ] = & SetMemberMatcher {
254
+ Path : merged [i ].Path ,
255
+ Child : merged [i ].Child .Merge (m .Child ),
256
+ }
257
+ } else {
258
+ merged = append (merged , m )
259
+ }
260
+ }
261
+ return NewSetMatcher (false , merged ... ) // sort happens here
262
+ }
263
+
264
+ // SetMemberMatcher defines a matcher that matches the members of a Set.
265
+ // SetMemberMatcher is structured much like the elements of a SetNodeMap, but
266
+ // with wildcard support.
267
+ type SetMemberMatcher struct {
268
+ // Path provides a matcher to match members of a Set.
269
+ // If Path is a wildcard, all members of a Set are included in the match.
270
+ // Otherwise, if any Path is Equal to a member of a Set, that member is
271
+ // included in the match and the children of that member are matched
272
+ // against the Child matcher.
273
+ Path PathElementMatcher
274
+
275
+ // Child provides a matcher to use for the children of matched members of a Set.
276
+ Child * SetMatcher
277
+ }
278
+
279
+ // PathElementMatcher defined a path matcher for a PathElement.
280
+ type PathElementMatcher struct {
281
+ // Wildcard indicates that all PathElements are matched by this matcher.
282
+ // If set, PathElement is ignored.
283
+ Wildcard bool
284
+
285
+ // PathElement indicates that a PathElement is matched if it is Equal
286
+ // to this PathElement.
287
+ PathElement
288
+ }
289
+
290
+ func (p PathElementMatcher ) Equals (p2 PathElementMatcher ) bool {
291
+ return p .Wildcard != p2 .Wildcard && p .PathElement .Equals (p2 .PathElement )
292
+ }
293
+
294
+ func (p PathElementMatcher ) Less (p2 PathElementMatcher ) bool {
295
+ if p .Wildcard && ! p2 .Wildcard {
296
+ return true
297
+ } else if p2 .Wildcard {
298
+ return false
299
+ }
300
+ return p .PathElement .Less (p2 .PathElement )
301
+ }
302
+
303
+ func (p PathElementMatcher ) Compare (p2 PathElementMatcher ) int {
304
+ if p .Wildcard && ! p2 .Wildcard {
305
+ return - 1
306
+ } else if p2 .Wildcard {
307
+ return 1
308
+ }
309
+ return p .PathElement .Compare (p2 .PathElement )
310
+ }
311
+
312
+ // FilterIncludeMatches returns a Set with only the field paths that match.
313
+ func (s * Set ) FilterIncludeMatches (pattern * SetMatcher ) * Set {
314
+ if pattern .wildcard {
315
+ return s
316
+ }
317
+
318
+ members := PathElementSet {}
319
+ for _ , m := range s .Members .members {
320
+ for _ , pm := range pattern .members {
321
+ if pm .Path .Wildcard || pm .Path .PathElement .Equals (m ) {
322
+ members .Insert (m )
323
+ break
324
+ }
325
+ }
326
+ }
327
+ return & Set {
328
+ Members : members ,
329
+ Children : * s .Children .FilterIncludeMatches (pattern ),
330
+ }
331
+ }
332
+
139
333
// Size returns the number of members of the set.
140
334
func (s * Set ) Size () int {
141
335
return s .Members .Size () + s .Children .Size ()
@@ -476,6 +670,33 @@ func (s *SetNodeMap) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.Ty
476
670
}
477
671
}
478
672
673
+ // FilterIncludeMatches returns a SetNodeMap with only the field paths that match the matcher.
674
+ func (s * SetNodeMap ) FilterIncludeMatches (pattern * SetMatcher ) * SetNodeMap {
675
+ if pattern .wildcard {
676
+ return s
677
+ }
678
+
679
+ var out sortedSetNode
680
+ for _ , member := range s .members {
681
+ for _ , c := range pattern .members {
682
+ if c .Path .Wildcard || c .Path .PathElement .Equals (member .pathElement ) {
683
+ childSet := member .set .FilterIncludeMatches (c .Child )
684
+ if childSet .Size () > 0 {
685
+ out = append (out , setNode {
686
+ pathElement : member .pathElement ,
687
+ set : childSet ,
688
+ })
689
+ }
690
+ break
691
+ }
692
+ }
693
+ }
694
+
695
+ return & SetNodeMap {
696
+ members : out ,
697
+ }
698
+ }
699
+
479
700
// Iterate calls f for each PathElement in the set.
480
701
func (s * SetNodeMap ) Iterate (f func (PathElement )) {
481
702
for _ , n := range s .members {
@@ -503,3 +724,59 @@ func (s *SetNodeMap) Leaves() *SetNodeMap {
503
724
}
504
725
return out
505
726
}
727
+
728
+ // Filter defines an interface for excluding field paths from a set.
729
+ // NewExcludeSetFilter can be used to create a filter that removes
730
+ // specific field paths and all of their children.
731
+ // NewIncludeMatcherFilter can be used to create a filter that removes all fields except
732
+ // the fields that match a field path matcher. PrefixMatcher and MakePrefixMatcherOrDie
733
+ // can be used to define field path patterns.
734
+ type Filter interface {
735
+ // Filter returns a filtered copy of the set.
736
+ Filter (* Set ) * Set
737
+ }
738
+
739
+ // NewExcludeSetFilter returns a filter that removes field paths in the exclude set.
740
+ func NewExcludeSetFilter (exclude * Set ) Filter {
741
+ return excludeFilter {exclude }
742
+ }
743
+
744
+ // NewExcludeFilterSetMap converts a map of APIVersion to exclude set to a map of APIVersion to exclude filters.
745
+ func NewExcludeFilterSetMap (resetFields map [APIVersion ]* Set ) map [APIVersion ]Filter {
746
+ result := make (map [APIVersion ]Filter )
747
+ for k , v := range resetFields {
748
+ result [k ] = excludeFilter {v }
749
+ }
750
+ return result
751
+ }
752
+
753
+ type excludeFilter struct {
754
+ excludeSet * Set
755
+ }
756
+
757
+ func (t excludeFilter ) Filter (set * Set ) * Set {
758
+ return set .RecursiveDifference (t .excludeSet )
759
+ }
760
+
761
+ // NewIncludeMatcherFilter returns a filter that only includes field paths that match.
762
+ // If no matchers are provided, the filter includes all field paths.
763
+ // PrefixMatcher and MakePrefixMatcherOrDie can help create basic matcher.
764
+ func NewIncludeMatcherFilter (matchers ... * SetMatcher ) Filter {
765
+ if len (matchers ) == 0 {
766
+ return includeMatcherFilter {& SetMatcher {wildcard : true }}
767
+ }
768
+ matcher := matchers [0 ]
769
+ for i := 1 ; i < len (matchers ); i ++ {
770
+ matcher = matcher .Merge (matchers [i ])
771
+ }
772
+
773
+ return includeMatcherFilter {matcher }
774
+ }
775
+
776
+ type includeMatcherFilter struct {
777
+ matcher * SetMatcher
778
+ }
779
+
780
+ func (pf includeMatcherFilter ) Filter (set * Set ) * Set {
781
+ return set .FilterIncludeMatches (pf .matcher )
782
+ }
0 commit comments