github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/api/types/filters/parse.go (about) 1 /* 2 Package filters provides tools for encoding a mapping of keys to a set of 3 multiple values. 4 */ 5 package filters // import "github.com/docker/docker/api/types/filters" 6 7 import ( 8 "encoding/json" 9 "regexp" 10 "strings" 11 12 "github.com/docker/docker/api/types/versions" 13 "github.com/pkg/errors" 14 ) 15 16 // Args stores a mapping of keys to a set of multiple values. 17 type Args struct { 18 fields map[string]map[string]bool 19 } 20 21 // KeyValuePair are used to initialize a new Args 22 type KeyValuePair struct { 23 Key string 24 Value string 25 } 26 27 // Arg creates a new KeyValuePair for initializing Args 28 func Arg(key, value string) KeyValuePair { 29 return KeyValuePair{Key: key, Value: value} 30 } 31 32 // NewArgs returns a new Args populated with the initial args 33 func NewArgs(initialArgs ...KeyValuePair) Args { 34 args := Args{fields: map[string]map[string]bool{}} 35 for _, arg := range initialArgs { 36 args.Add(arg.Key, arg.Value) 37 } 38 return args 39 } 40 41 // Keys returns all the keys in list of Args 42 func (args Args) Keys() []string { 43 keys := make([]string, 0, len(args.fields)) 44 for k := range args.fields { 45 keys = append(keys, k) 46 } 47 return keys 48 } 49 50 // MarshalJSON returns a JSON byte representation of the Args 51 func (args Args) MarshalJSON() ([]byte, error) { 52 if len(args.fields) == 0 { 53 return []byte{}, nil 54 } 55 return json.Marshal(args.fields) 56 } 57 58 // ToJSON returns the Args as a JSON encoded string 59 func ToJSON(a Args) (string, error) { 60 if a.Len() == 0 { 61 return "", nil 62 } 63 buf, err := json.Marshal(a) 64 return string(buf), err 65 } 66 67 // ToParamWithVersion encodes Args as a JSON string. If version is less than 1.22 68 // then the encoded format will use an older legacy format where the values are a 69 // list of strings, instead of a set. 70 // 71 // Deprecated: do not use in any new code; use ToJSON instead 72 func ToParamWithVersion(version string, a Args) (string, error) { 73 if a.Len() == 0 { 74 return "", nil 75 } 76 77 if version != "" && versions.LessThan(version, "1.22") { 78 buf, err := json.Marshal(convertArgsToSlice(a.fields)) 79 return string(buf), err 80 } 81 82 return ToJSON(a) 83 } 84 85 // FromJSON decodes a JSON encoded string into Args 86 func FromJSON(p string) (Args, error) { 87 args := NewArgs() 88 89 if p == "" { 90 return args, nil 91 } 92 93 raw := []byte(p) 94 err := json.Unmarshal(raw, &args) 95 if err == nil { 96 return args, nil 97 } 98 99 // Fallback to parsing arguments in the legacy slice format 100 deprecated := map[string][]string{} 101 if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil { 102 return args, invalidFilter{errors.Wrap(err, "invalid filter")} 103 } 104 105 args.fields = deprecatedArgs(deprecated) 106 return args, nil 107 } 108 109 // UnmarshalJSON populates the Args from JSON encode bytes 110 func (args Args) UnmarshalJSON(raw []byte) error { 111 if len(raw) == 0 { 112 return nil 113 } 114 return json.Unmarshal(raw, &args.fields) 115 } 116 117 // Get returns the list of values associated with the key 118 func (args Args) Get(key string) []string { 119 values := args.fields[key] 120 if values == nil { 121 return make([]string, 0) 122 } 123 slice := make([]string, 0, len(values)) 124 for key := range values { 125 slice = append(slice, key) 126 } 127 return slice 128 } 129 130 // Add a new value to the set of values 131 func (args Args) Add(key, value string) { 132 if _, ok := args.fields[key]; ok { 133 args.fields[key][value] = true 134 } else { 135 args.fields[key] = map[string]bool{value: true} 136 } 137 } 138 139 // Del removes a value from the set 140 func (args Args) Del(key, value string) { 141 if _, ok := args.fields[key]; ok { 142 delete(args.fields[key], value) 143 if len(args.fields[key]) == 0 { 144 delete(args.fields, key) 145 } 146 } 147 } 148 149 // Len returns the number of keys in the mapping 150 func (args Args) Len() int { 151 return len(args.fields) 152 } 153 154 // MatchKVList returns true if all the pairs in sources exist as key=value 155 // pairs in the mapping at key, or if there are no values at key. 156 func (args Args) MatchKVList(key string, sources map[string]string) bool { 157 fieldValues := args.fields[key] 158 159 // do not filter if there is no filter set or cannot determine filter 160 if len(fieldValues) == 0 { 161 return true 162 } 163 164 if len(sources) == 0 { 165 return false 166 } 167 168 for value := range fieldValues { 169 testKV := strings.SplitN(value, "=", 2) 170 171 v, ok := sources[testKV[0]] 172 if !ok { 173 return false 174 } 175 if len(testKV) == 2 && testKV[1] != v { 176 return false 177 } 178 } 179 180 return true 181 } 182 183 // Match returns true if any of the values at key match the source string 184 func (args Args) Match(field, source string) bool { 185 if args.ExactMatch(field, source) { 186 return true 187 } 188 189 fieldValues := args.fields[field] 190 for name2match := range fieldValues { 191 match, err := regexp.MatchString(name2match, source) 192 if err != nil { 193 continue 194 } 195 if match { 196 return true 197 } 198 } 199 return false 200 } 201 202 // ExactMatch returns true if the source matches exactly one of the values. 203 func (args Args) ExactMatch(key, source string) bool { 204 fieldValues, ok := args.fields[key] 205 // do not filter if there is no filter set or cannot determine filter 206 if !ok || len(fieldValues) == 0 { 207 return true 208 } 209 210 // try to match full name value to avoid O(N) regular expression matching 211 return fieldValues[source] 212 } 213 214 // UniqueExactMatch returns true if there is only one value and the source 215 // matches exactly the value. 216 func (args Args) UniqueExactMatch(key, source string) bool { 217 fieldValues := args.fields[key] 218 // do not filter if there is no filter set or cannot determine filter 219 if len(fieldValues) == 0 { 220 return true 221 } 222 if len(args.fields[key]) != 1 { 223 return false 224 } 225 226 // try to match full name value to avoid O(N) regular expression matching 227 return fieldValues[source] 228 } 229 230 // FuzzyMatch returns true if the source matches exactly one value, or the 231 // source has one of the values as a prefix. 232 func (args Args) FuzzyMatch(key, source string) bool { 233 if args.ExactMatch(key, source) { 234 return true 235 } 236 237 fieldValues := args.fields[key] 238 for prefix := range fieldValues { 239 if strings.HasPrefix(source, prefix) { 240 return true 241 } 242 } 243 return false 244 } 245 246 // Contains returns true if the key exists in the mapping 247 func (args Args) Contains(field string) bool { 248 _, ok := args.fields[field] 249 return ok 250 } 251 252 type invalidFilter struct{ error } 253 254 func (e invalidFilter) Error() string { 255 return e.error.Error() 256 } 257 258 func (invalidFilter) InvalidParameter() {} 259 260 // Validate compared the set of accepted keys against the keys in the mapping. 261 // An error is returned if any mapping keys are not in the accepted set. 262 func (args Args) Validate(accepted map[string]bool) error { 263 for name := range args.fields { 264 if !accepted[name] { 265 return invalidFilter{errors.New("invalid filter '" + name + "'")} 266 } 267 } 268 return nil 269 } 270 271 // WalkValues iterates over the list of values for a key in the mapping and calls 272 // op() for each value. If op returns an error the iteration stops and the 273 // error is returned. 274 func (args Args) WalkValues(field string, op func(value string) error) error { 275 if _, ok := args.fields[field]; !ok { 276 return nil 277 } 278 for v := range args.fields[field] { 279 if err := op(v); err != nil { 280 return err 281 } 282 } 283 return nil 284 } 285 286 // Clone returns a copy of args. 287 func (args Args) Clone() (newArgs Args) { 288 newArgs.fields = make(map[string]map[string]bool, len(args.fields)) 289 for k, m := range args.fields { 290 var mm map[string]bool 291 if m != nil { 292 mm = make(map[string]bool, len(m)) 293 for kk, v := range m { 294 mm[kk] = v 295 } 296 } 297 newArgs.fields[k] = mm 298 } 299 return newArgs 300 } 301 302 func deprecatedArgs(d map[string][]string) map[string]map[string]bool { 303 m := map[string]map[string]bool{} 304 for k, v := range d { 305 values := map[string]bool{} 306 for _, vv := range v { 307 values[vv] = true 308 } 309 m[k] = values 310 } 311 return m 312 } 313 314 func convertArgsToSlice(f map[string]map[string]bool) map[string][]string { 315 m := map[string][]string{} 316 for k, v := range f { 317 values := []string{} 318 for kk := range v { 319 if v[kk] { 320 values = append(values, kk) 321 } 322 } 323 m[k] = values 324 } 325 return m 326 }