github.com/shiroyuki/docker@v1.9.0/pkg/parsers/filters/parse.go (about)

     1  // Package filters provides helper function to parse and handle command line
     2  // filter, used for example in docker ps or docker images commands.
     3  package filters
     4  
     5  import (
     6  	"encoding/json"
     7  	"errors"
     8  	"regexp"
     9  	"strings"
    10  )
    11  
    12  // Args stores filter arguments as map key:{array of values}.
    13  // It contains a aggregation of the list of arguments (which are in the form
    14  // of -f 'key=value') based on the key, and store values for the same key
    15  // in an slice.
    16  // e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
    17  // the args will be {'label': {'label1=1','label2=2'}, 'image.name', {'ubuntu'}}
    18  type Args map[string][]string
    19  
    20  // ParseFlag parses the argument to the filter flag. Like
    21  //
    22  //   `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
    23  //
    24  // If prev map is provided, then it is appended to, and returned. By default a new
    25  // map is created.
    26  func ParseFlag(arg string, prev Args) (Args, error) {
    27  	filters := prev
    28  	if prev == nil {
    29  		filters = Args{}
    30  	}
    31  	if len(arg) == 0 {
    32  		return filters, nil
    33  	}
    34  
    35  	if !strings.Contains(arg, "=") {
    36  		return filters, ErrBadFormat
    37  	}
    38  
    39  	f := strings.SplitN(arg, "=", 2)
    40  	name := strings.ToLower(strings.TrimSpace(f[0]))
    41  	value := strings.TrimSpace(f[1])
    42  	filters[name] = append(filters[name], value)
    43  
    44  	return filters, nil
    45  }
    46  
    47  // ErrBadFormat is an error returned in case of bad format for a filter.
    48  var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
    49  
    50  // ToParam packs the Args into an string for easy transport from client to server.
    51  func ToParam(a Args) (string, error) {
    52  	// this way we don't URL encode {}, just empty space
    53  	if len(a) == 0 {
    54  		return "", nil
    55  	}
    56  
    57  	buf, err := json.Marshal(a)
    58  	if err != nil {
    59  		return "", err
    60  	}
    61  	return string(buf), nil
    62  }
    63  
    64  // FromParam unpacks the filter Args.
    65  func FromParam(p string) (Args, error) {
    66  	args := Args{}
    67  	if len(p) == 0 {
    68  		return args, nil
    69  	}
    70  	if err := json.NewDecoder(strings.NewReader(p)).Decode(&args); err != nil {
    71  		return nil, err
    72  	}
    73  	return args, nil
    74  }
    75  
    76  // MatchKVList returns true if the values for the specified field maches the ones
    77  // from the sources.
    78  // e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
    79  //      field is 'label' and sources are {'label':{'label1=1','label2=2','label3=3'}}
    80  //      it returns true.
    81  func (filters Args) MatchKVList(field string, sources map[string]string) bool {
    82  	fieldValues := filters[field]
    83  
    84  	//do not filter if there is no filter set or cannot determine filter
    85  	if len(fieldValues) == 0 {
    86  		return true
    87  	}
    88  
    89  	if sources == nil || len(sources) == 0 {
    90  		return false
    91  	}
    92  
    93  outer:
    94  	for _, name2match := range fieldValues {
    95  		testKV := strings.SplitN(name2match, "=", 2)
    96  
    97  		for k, v := range sources {
    98  			if len(testKV) == 1 {
    99  				if k == testKV[0] {
   100  					continue outer
   101  				}
   102  			} else if k == testKV[0] && v == testKV[1] {
   103  				continue outer
   104  			}
   105  		}
   106  
   107  		return false
   108  	}
   109  
   110  	return true
   111  }
   112  
   113  // Match returns true if the values for the specified field matches the source string
   114  // e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
   115  //      field is 'image.name' and source is 'ubuntu'
   116  //      it returns true.
   117  func (filters Args) Match(field, source string) bool {
   118  	fieldValues := filters[field]
   119  
   120  	//do not filter if there is no filter set or cannot determine filter
   121  	if len(fieldValues) == 0 {
   122  		return true
   123  	}
   124  	for _, name2match := range fieldValues {
   125  		match, err := regexp.MatchString(name2match, source)
   126  		if err != nil {
   127  			continue
   128  		}
   129  		if match {
   130  			return true
   131  		}
   132  	}
   133  	return false
   134  }