/vendor/github.com/docker/engine-api/types/filters/parse.go

https://gitlab.com/admin-github-cloud/quayctl · Go · 257 lines · 175 code · 36 blank · 46 comment · 52 complexity · f891a05fe97dbf074c8108aa19922eb0 MD5 · raw file

  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. import (
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "regexp"
  9. "strings"
  10. )
  11. // Args stores filter arguments as map key:{map key: bool}.
  12. // It contains a aggregation of the map of arguments (which are in the form
  13. // of -f 'key=value') based on the key, and store values for the same key
  14. // in an map with string keys and boolean values.
  15. // e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
  16. // the args will be {"image.name":{"ubuntu":true},"label":{"label1=1":true,"label2=2":true}}
  17. type Args struct {
  18. fields map[string]map[string]bool
  19. }
  20. // NewArgs initializes a new Args struct.
  21. func NewArgs() Args {
  22. return Args{fields: map[string]map[string]bool{}}
  23. }
  24. // ParseFlag parses the argument to the filter flag. Like
  25. //
  26. // `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
  27. //
  28. // If prev map is provided, then it is appended to, and returned. By default a new
  29. // map is created.
  30. func ParseFlag(arg string, prev Args) (Args, error) {
  31. filters := prev
  32. if len(arg) == 0 {
  33. return filters, nil
  34. }
  35. if !strings.Contains(arg, "=") {
  36. return filters, ErrBadFormat
  37. }
  38. f := strings.SplitN(arg, "=", 2)
  39. name := strings.ToLower(strings.TrimSpace(f[0]))
  40. value := strings.TrimSpace(f[1])
  41. filters.Add(name, value)
  42. return filters, nil
  43. }
  44. // ErrBadFormat is an error returned in case of bad format for a filter.
  45. var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
  46. // ToParam packs the Args into an string for easy transport from client to server.
  47. func ToParam(a Args) (string, error) {
  48. // this way we don't URL encode {}, just empty space
  49. if a.Len() == 0 {
  50. return "", nil
  51. }
  52. buf, err := json.Marshal(a.fields)
  53. if err != nil {
  54. return "", err
  55. }
  56. return string(buf), nil
  57. }
  58. // FromParam unpacks the filter Args.
  59. func FromParam(p string) (Args, error) {
  60. if len(p) == 0 {
  61. return NewArgs(), nil
  62. }
  63. r := strings.NewReader(p)
  64. d := json.NewDecoder(r)
  65. m := map[string]map[string]bool{}
  66. if err := d.Decode(&m); err != nil {
  67. r.Seek(0, 0)
  68. // Allow parsing old arguments in slice format.
  69. // Because other libraries might be sending them in this format.
  70. deprecated := map[string][]string{}
  71. if deprecatedErr := d.Decode(&deprecated); deprecatedErr == nil {
  72. m = deprecatedArgs(deprecated)
  73. } else {
  74. return NewArgs(), err
  75. }
  76. }
  77. return Args{m}, nil
  78. }
  79. // Get returns the list of values associates with a field.
  80. // It returns a slice of strings to keep backwards compatibility with old code.
  81. func (filters Args) Get(field string) []string {
  82. values := filters.fields[field]
  83. if values == nil {
  84. return make([]string, 0)
  85. }
  86. slice := make([]string, 0, len(values))
  87. for key := range values {
  88. slice = append(slice, key)
  89. }
  90. return slice
  91. }
  92. // Add adds a new value to a filter field.
  93. func (filters Args) Add(name, value string) {
  94. if _, ok := filters.fields[name]; ok {
  95. filters.fields[name][value] = true
  96. } else {
  97. filters.fields[name] = map[string]bool{value: true}
  98. }
  99. }
  100. // Del removes a value from a filter field.
  101. func (filters Args) Del(name, value string) {
  102. if _, ok := filters.fields[name]; ok {
  103. delete(filters.fields[name], value)
  104. }
  105. }
  106. // Len returns the number of fields in the arguments.
  107. func (filters Args) Len() int {
  108. return len(filters.fields)
  109. }
  110. // MatchKVList returns true if the values for the specified field matches the ones
  111. // from the sources.
  112. // e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
  113. // field is 'label' and sources are {'label1': '1', 'label2': '2'}
  114. // it returns true.
  115. func (filters Args) MatchKVList(field string, sources map[string]string) bool {
  116. fieldValues := filters.fields[field]
  117. //do not filter if there is no filter set or cannot determine filter
  118. if len(fieldValues) == 0 {
  119. return true
  120. }
  121. if sources == nil || len(sources) == 0 {
  122. return false
  123. }
  124. for name2match := range fieldValues {
  125. testKV := strings.SplitN(name2match, "=", 2)
  126. v, ok := sources[testKV[0]]
  127. if !ok {
  128. return false
  129. }
  130. if len(testKV) == 2 && testKV[1] != v {
  131. return false
  132. }
  133. }
  134. return true
  135. }
  136. // Match returns true if the values for the specified field matches the source string
  137. // e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
  138. // field is 'image.name' and source is 'ubuntu'
  139. // it returns true.
  140. func (filters Args) Match(field, source string) bool {
  141. if filters.ExactMatch(field, source) {
  142. return true
  143. }
  144. fieldValues := filters.fields[field]
  145. for name2match := range fieldValues {
  146. match, err := regexp.MatchString(name2match, source)
  147. if err != nil {
  148. continue
  149. }
  150. if match {
  151. return true
  152. }
  153. }
  154. return false
  155. }
  156. // ExactMatch returns true if the source matches exactly one of the filters.
  157. func (filters Args) ExactMatch(field, source string) bool {
  158. fieldValues, ok := filters.fields[field]
  159. //do not filter if there is no filter set or cannot determine filter
  160. if !ok || len(fieldValues) == 0 {
  161. return true
  162. }
  163. // try to march full name value to avoid O(N) regular expression matching
  164. if fieldValues[source] {
  165. return true
  166. }
  167. return false
  168. }
  169. // FuzzyMatch returns true if the source matches exactly one of the filters,
  170. // or the source has one of the filters as a prefix.
  171. func (filters Args) FuzzyMatch(field, source string) bool {
  172. if filters.ExactMatch(field, source) {
  173. return true
  174. }
  175. fieldValues := filters.fields[field]
  176. for prefix := range fieldValues {
  177. if strings.HasPrefix(source, prefix) {
  178. return true
  179. }
  180. }
  181. return false
  182. }
  183. // Include returns true if the name of the field to filter is in the filters.
  184. func (filters Args) Include(field string) bool {
  185. _, ok := filters.fields[field]
  186. return ok
  187. }
  188. // Validate ensures that all the fields in the filter are valid.
  189. // It returns an error as soon as it finds an invalid field.
  190. func (filters Args) Validate(accepted map[string]bool) error {
  191. for name := range filters.fields {
  192. if !accepted[name] {
  193. return fmt.Errorf("Invalid filter '%s'", name)
  194. }
  195. }
  196. return nil
  197. }
  198. // WalkValues iterates over the list of filtered values for a field.
  199. // It stops the iteration if it finds an error and it returns that error.
  200. func (filters Args) WalkValues(field string, op func(value string) error) error {
  201. if _, ok := filters.fields[field]; !ok {
  202. return nil
  203. }
  204. for v := range filters.fields[field] {
  205. if err := op(v); err != nil {
  206. return err
  207. }
  208. }
  209. return nil
  210. }
  211. func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
  212. m := map[string]map[string]bool{}
  213. for k, v := range d {
  214. values := map[string]bool{}
  215. for _, vv := range v {
  216. values[vv] = true
  217. }
  218. m[k] = values
  219. }
  220. return m
  221. }