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  }