github.com/lalkh/containerd@v1.4.3/filters/parser.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package filters
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  
    23  	"github.com/containerd/containerd/errdefs"
    24  	"github.com/pkg/errors"
    25  )
    26  
    27  /*
    28  Parse the strings into a filter that may be used with an adaptor.
    29  
    30  The filter is made up of zero or more selectors.
    31  
    32  The format is a comma separated list of expressions, in the form of
    33  `<fieldpath><op><value>`, known as selectors. All selectors must match the
    34  target object for the filter to be true.
    35  
    36  We define the operators "==" for equality, "!=" for not equal and "~=" for a
    37  regular expression. If the operator and value are not present, the matcher will
    38  test for the presence of a value, as defined by the target object.
    39  
    40  The formal grammar is as follows:
    41  
    42  selectors := selector ("," selector)*
    43  selector  := fieldpath (operator value)
    44  fieldpath := field ('.' field)*
    45  field     := quoted | [A-Za-z] [A-Za-z0-9_]+
    46  operator  := "==" | "!=" | "~="
    47  value     := quoted | [^\s,]+
    48  quoted    := <go string syntax>
    49  
    50  */
    51  func Parse(s string) (Filter, error) {
    52  	// special case empty to match all
    53  	if s == "" {
    54  		return Always, nil
    55  	}
    56  
    57  	p := parser{input: s}
    58  	return p.parse()
    59  }
    60  
    61  // ParseAll parses each filter in ss and returns a filter that will return true
    62  // if any filter matches the expression.
    63  //
    64  // If no filters are provided, the filter will match anything.
    65  func ParseAll(ss ...string) (Filter, error) {
    66  	if len(ss) == 0 {
    67  		return Always, nil
    68  	}
    69  
    70  	var fs []Filter
    71  	for _, s := range ss {
    72  		f, err := Parse(s)
    73  		if err != nil {
    74  			return nil, errors.Wrap(errdefs.ErrInvalidArgument, err.Error())
    75  		}
    76  
    77  		fs = append(fs, f)
    78  	}
    79  
    80  	return Any(fs), nil
    81  }
    82  
    83  type parser struct {
    84  	input   string
    85  	scanner scanner
    86  }
    87  
    88  func (p *parser) parse() (Filter, error) {
    89  	p.scanner.init(p.input)
    90  
    91  	ss, err := p.selectors()
    92  	if err != nil {
    93  		return nil, errors.Wrap(err, "filters")
    94  	}
    95  
    96  	return ss, nil
    97  }
    98  
    99  func (p *parser) selectors() (Filter, error) {
   100  	s, err := p.selector()
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	ss := All{s}
   106  
   107  loop:
   108  	for {
   109  		tok := p.scanner.peek()
   110  		switch tok {
   111  		case ',':
   112  			pos, tok, _ := p.scanner.scan()
   113  			if tok != tokenSeparator {
   114  				return nil, p.mkerr(pos, "expected a separator")
   115  			}
   116  
   117  			s, err := p.selector()
   118  			if err != nil {
   119  				return nil, err
   120  			}
   121  
   122  			ss = append(ss, s)
   123  		case tokenEOF:
   124  			break loop
   125  		default:
   126  			return nil, p.mkerr(p.scanner.ppos, "unexpected input: %v", string(tok))
   127  		}
   128  	}
   129  
   130  	return ss, nil
   131  }
   132  
   133  func (p *parser) selector() (selector, error) {
   134  	fieldpath, err := p.fieldpath()
   135  	if err != nil {
   136  		return selector{}, err
   137  	}
   138  
   139  	switch p.scanner.peek() {
   140  	case ',', tokenSeparator, tokenEOF:
   141  		return selector{
   142  			fieldpath: fieldpath,
   143  			operator:  operatorPresent,
   144  		}, nil
   145  	}
   146  
   147  	op, err := p.operator()
   148  	if err != nil {
   149  		return selector{}, err
   150  	}
   151  
   152  	var allowAltQuotes bool
   153  	if op == operatorMatches {
   154  		allowAltQuotes = true
   155  	}
   156  
   157  	value, err := p.value(allowAltQuotes)
   158  	if err != nil {
   159  		if err == io.EOF {
   160  			return selector{}, io.ErrUnexpectedEOF
   161  		}
   162  		return selector{}, err
   163  	}
   164  
   165  	return selector{
   166  		fieldpath: fieldpath,
   167  		value:     value,
   168  		operator:  op,
   169  	}, nil
   170  }
   171  
   172  func (p *parser) fieldpath() ([]string, error) {
   173  	f, err := p.field()
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	fs := []string{f}
   179  loop:
   180  	for {
   181  		tok := p.scanner.peek() // lookahead to consume field separator
   182  
   183  		switch tok {
   184  		case '.':
   185  			pos, tok, _ := p.scanner.scan() // consume separator
   186  			if tok != tokenSeparator {
   187  				return nil, p.mkerr(pos, "expected a field separator (`.`)")
   188  			}
   189  
   190  			f, err := p.field()
   191  			if err != nil {
   192  				return nil, err
   193  			}
   194  
   195  			fs = append(fs, f)
   196  		default:
   197  			// let the layer above handle the other bad cases.
   198  			break loop
   199  		}
   200  	}
   201  
   202  	return fs, nil
   203  }
   204  
   205  func (p *parser) field() (string, error) {
   206  	pos, tok, s := p.scanner.scan()
   207  	switch tok {
   208  	case tokenField:
   209  		return s, nil
   210  	case tokenQuoted:
   211  		return p.unquote(pos, s, false)
   212  	case tokenIllegal:
   213  		return "", p.mkerr(pos, p.scanner.err)
   214  	}
   215  
   216  	return "", p.mkerr(pos, "expected field or quoted")
   217  }
   218  
   219  func (p *parser) operator() (operator, error) {
   220  	pos, tok, s := p.scanner.scan()
   221  	switch tok {
   222  	case tokenOperator:
   223  		switch s {
   224  		case "==":
   225  			return operatorEqual, nil
   226  		case "!=":
   227  			return operatorNotEqual, nil
   228  		case "~=":
   229  			return operatorMatches, nil
   230  		default:
   231  			return 0, p.mkerr(pos, "unsupported operator %q", s)
   232  		}
   233  	case tokenIllegal:
   234  		return 0, p.mkerr(pos, p.scanner.err)
   235  	}
   236  
   237  	return 0, p.mkerr(pos, `expected an operator ("=="|"!="|"~=")`)
   238  }
   239  
   240  func (p *parser) value(allowAltQuotes bool) (string, error) {
   241  	pos, tok, s := p.scanner.scan()
   242  
   243  	switch tok {
   244  	case tokenValue, tokenField:
   245  		return s, nil
   246  	case tokenQuoted:
   247  		return p.unquote(pos, s, allowAltQuotes)
   248  	case tokenIllegal:
   249  		return "", p.mkerr(pos, p.scanner.err)
   250  	}
   251  
   252  	return "", p.mkerr(pos, "expected value or quoted")
   253  }
   254  
   255  func (p *parser) unquote(pos int, s string, allowAlts bool) (string, error) {
   256  	if !allowAlts && s[0] != '\'' && s[0] != '"' {
   257  		return "", p.mkerr(pos, "invalid quote encountered")
   258  	}
   259  
   260  	uq, err := unquote(s)
   261  	if err != nil {
   262  		return "", p.mkerr(pos, "unquoting failed: %v", err)
   263  	}
   264  
   265  	return uq, nil
   266  }
   267  
   268  type parseError struct {
   269  	input string
   270  	pos   int
   271  	msg   string
   272  }
   273  
   274  func (pe parseError) Error() string {
   275  	if pe.pos < len(pe.input) {
   276  		before := pe.input[:pe.pos]
   277  		location := pe.input[pe.pos : pe.pos+1] // need to handle end
   278  		after := pe.input[pe.pos+1:]
   279  
   280  		return fmt.Sprintf("[%s >|%s|< %s]: %v", before, location, after, pe.msg)
   281  	}
   282  
   283  	return fmt.Sprintf("[%s]: %v", pe.input, pe.msg)
   284  }
   285  
   286  func (p *parser) mkerr(pos int, format string, args ...interface{}) error {
   287  	return errors.Wrap(parseError{
   288  		input: p.input,
   289  		pos:   pos,
   290  		msg:   fmt.Sprintf(format, args...),
   291  	}, "parse error")
   292  }