@@ -61,10 +61,19 @@ func NewOptions[T any](opt ...Option[T]) (Options[T], error) {
61
61
return opts , nil
62
62
}
63
63
64
+ // ValueProvider extracts a single string value from an item of type T.
64
65
type ValueProvider [T any ] func (T ) string
65
66
67
+ // ValuesProvider extracts a slice of string values from an item of type T.
66
68
type ValuesProvider [T any ] func (T ) []string
67
69
70
+ // Equals returns a Predicate that checks if the value extracted by the provider
71
+ // exactly matches the filter value (case-insensitive, normalized).
72
+ //
73
+ // Example:
74
+ //
75
+ // predicate := Equals(options.SourceProvider),
76
+ // result := predicate(pkg, "github") // true if pkg.Source equals "github"
68
77
func Equals [T any ](provider ValueProvider [T ]) Predicate [T ] {
69
78
return func (item T , val string ) bool {
70
79
actual := NormalizeString (provider (item ))
@@ -73,18 +82,84 @@ func Equals[T any](provider ValueProvider[T]) Predicate[T] {
73
82
}
74
83
}
75
84
76
- func Contains [T any ](provider ValueProvider [T ]) Predicate [T ] {
85
+ // Partial returns a Predicate that checks if the value extracted by the provider
86
+ // contains the filter value as a substring (case-insensitive, normalized).
87
+ //
88
+ // Example:
89
+ //
90
+ // predicate := Partial(options.VersionProvider),
91
+ // result := predicate(pkg, "1.2") // true if pkg.Version contains "1.2"
92
+ func Partial [T any ](provider ValueProvider [T ]) Predicate [T ] {
77
93
return func (item T , val string ) bool {
78
94
actual := NormalizeString (provider (item ))
79
95
expected := NormalizeString (val )
80
96
return strings .Contains (actual , expected )
81
97
}
82
98
}
83
99
84
- func ContainsOnly [T any ](provider ValuesProvider [T ]) Predicate [T ] {
100
+ // PartialAll returns a Predicate that checks if *ALL* comma-separated values in the filter string are found
101
+ // as substrings within provided values (case-insensitive, normalized).
102
+ // Functionally similar to Partial, but operates on a ValuesProvider, and expects the filter to be comma-separated.
103
+ //
104
+ // Example:
105
+ //
106
+ // predicate := PartialAll(options.ToolsProvider),
107
+ // result := predicate(pkg, "get_current_time,convert_time") // true if pkg.Tools contains values with "get_current_time" and "convert_time" as substrings
108
+ func PartialAll [T any ](provider ValuesProvider [T ]) Predicate [T ] {
109
+ return func (item T , val string ) bool {
110
+ required := NormalizeSlice (strings .Split (val , "," ))
111
+ actual := NormalizeSlice (provider (item ))
112
+
113
+ for _ , v := range required {
114
+ found := false
115
+ for _ , a := range actual {
116
+ if strings .Contains (a , v ) {
117
+ found = true
118
+ break
119
+ }
120
+ }
121
+ if ! found {
122
+ return false
123
+ }
124
+ }
125
+ return true
126
+ }
127
+ }
128
+
129
+ // EqualsAny returns a Predicate that checks if *ANY* of the values from the supplied providers are equal to the
130
+ // filter value (case-insensitive, normalized).
131
+ // Functionally similar to Equals, but operates on one or more ValueProvider.
132
+ //
133
+ // Example:
134
+ //
135
+ // predicate := EqualsAny(options.ToolsProvider),
136
+ // result := predicate(pkg, "get_current_time,convert_time") // true if pkg.Tools contains values "get_current_time" or "convert_time"
137
+ func EqualsAny [T any ](providers ... ValueProvider [T ]) Predicate [T ] {
138
+ return func (item T , val string ) bool {
139
+ q := NormalizeString (val )
140
+ for _ , p := range providers {
141
+ actual := NormalizeString (p (item ))
142
+ if strings .Contains (actual , q ) {
143
+ return true
144
+ }
145
+ }
146
+ return false
147
+ }
148
+ }
149
+
150
+ // HasOnly returns a Predicate that checks if the values extracted by the provider are a subset of
151
+ // the comma-separated values in the filter string (case-insensitive, normalized).
152
+ // Returns true only if *ALL* extracted values are present in the filter list.
153
+ //
154
+ // Example:
155
+ //
156
+ // predicate := HasOnly(options.ToolsProvider),
157
+ // result := predicate(pkg, "get_current_time,convert_time") // true if pkg.Tools only contains tools from the list
158
+ func HasOnly [T any ](provider ValuesProvider [T ]) Predicate [T ] {
85
159
return func (item T , val string ) bool {
86
160
required := strings .Split (val , "," )
87
161
expected := make (map [string ]struct {}, len (required ))
162
+
88
163
for _ , v := range required {
89
164
expected [NormalizeString (v )] = struct {}{}
90
165
}
@@ -98,31 +173,48 @@ func ContainsOnly[T any](provider ValuesProvider[T]) Predicate[T] {
98
173
}
99
174
}
100
175
101
- func ContainsAll [T any ](provider ValuesProvider [T ]) Predicate [T ] {
176
+ // HasAll returns a Predicate that checks if the values extracted by the provider include *ALL*
177
+ // of the comma-separated values in the filter string (case-insensitive, normalized)..
178
+ // Returns true only if *ALL* required values are present in the extracted values.
179
+ //
180
+ // Example:
181
+ //
182
+ // predicate := HasAll(options.ToolsProvider),
183
+ // result := predicate(pkg, "get_current_time,convert_time") // true if pkg.Tools contains both "get_current_time" and "convert_time"
184
+ func HasAll [T any ](provider ValuesProvider [T ]) Predicate [T ] {
102
185
return func (item T , val string ) bool {
103
186
required := NormalizeSlice (strings .Split (val , "," ))
104
187
actual := provider (item )
188
+ allowed := make (map [string ]struct {}, len (actual ))
105
189
106
- actualSet := make (map [string ]struct {}, len (actual ))
107
190
for _ , v := range actual {
108
- actualSet [NormalizeString (v )] = struct {}{}
191
+ allowed [NormalizeString (v )] = struct {}{}
109
192
}
110
193
111
194
for _ , r := range required {
112
- if _ , ok := actualSet [r ]; ! ok {
195
+ if _ , ok := allowed [r ]; ! ok {
113
196
return false
114
197
}
115
198
}
116
199
return true
117
200
}
118
201
}
119
202
120
- func ContainsAny [T any ](provider ValuesProvider [T ]) Predicate [T ] {
203
+ // HasAny returns a Predicate that checks if the values extracted by the provider include *ANY* of
204
+ // the comma-separated values in the filter string (case-insensitive, normalized).
205
+ // Returns true if at least one required value is present in the extracted values.
206
+ //
207
+ // Example:
208
+ //
209
+ // predicate := HasAny(options.ToolsProvider),
210
+ // result := predicate(pkg, "get_current_time,convert_time") // true if pkg.Tools contains either "get_current_time" or "convert_time"
211
+ func HasAny [T any ](provider ValuesProvider [T ]) Predicate [T ] {
121
212
return func (item T , val string ) bool {
122
- required := strings .Split (val , "," )
213
+ required := NormalizeSlice ( strings .Split (val , "," ) )
123
214
allowed := make (map [string ]struct {}, len (required ))
215
+
124
216
for _ , v := range required {
125
- allowed [NormalizeString ( v ) ] = struct {}{}
217
+ allowed [v ] = struct {}{}
126
218
}
127
219
128
220
for _ , v := range provider (item ) {
@@ -134,19 +226,6 @@ func ContainsAny[T any](provider ValuesProvider[T]) Predicate[T] {
134
226
}
135
227
}
136
228
137
- func OrContains [T any ](providers ... ValueProvider [T ]) Predicate [T ] {
138
- return func (item T , val string ) bool {
139
- q := NormalizeString (val )
140
- for _ , p := range providers {
141
- actual := NormalizeString (p (item ))
142
- if strings .Contains (actual , q ) {
143
- return true
144
- }
145
- }
146
- return false
147
- }
148
- }
149
-
150
229
// WithMatchers adds or overrides matchers.
151
230
func WithMatchers [T any ](m map [string ]Predicate [T ]) Option [T ] {
152
231
return func (o * Options [T ]) error {
0 commit comments