| // Copyright 2022 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package parse |
| |
| import ( |
| "fmt" |
| "regexp" |
| "strings" |
| ) |
| |
| // A Filter is a node in the boolean filter. It can either be a |
| // FilterOp or a FilterMatch. |
| type Filter interface { |
| isFilter() |
| String() string |
| } |
| |
| // A FilterMatch is a leaf in a Filter tree that tests a specific key |
| // for a match. |
| type FilterMatch struct { |
| Key string |
| |
| // Regexp is the regular expression to match against the |
| // value. This may be nil, in which case this is a literal |
| // match against Lit. |
| Regexp *regexp.Regexp |
| // Lit is the literal value to match against the value if Regexp |
| // is nil. |
| Lit string |
| |
| // Off is the byte offset of the key in the original query, |
| // for error reporting. |
| Off int |
| } |
| |
| func (q *FilterMatch) isFilter() {} |
| func (q *FilterMatch) String() string { |
| if q.Regexp != nil { |
| return quoteWord(q.Key) + ":/" + q.Regexp.String() + "/" |
| } |
| return quoteWord(q.Key) + ":" + quoteWord(q.Lit) |
| } |
| |
| // Match returns whether q matches the given value of q.Key. |
| func (q *FilterMatch) Match(value []byte) bool { |
| if q.Regexp != nil { |
| return q.Regexp.Match(value) |
| } |
| return q.Lit == string(value) |
| } |
| |
| // MatchString returns whether q matches the given value of q.Key. |
| func (q *FilterMatch) MatchString(value string) bool { |
| if q.Regexp != nil { |
| return q.Regexp.MatchString(value) |
| } |
| return q.Lit == value |
| } |
| |
| // A FilterOp is a boolean operator in the Filter tree. OpNot must have |
| // exactly one child node. OpAnd and OpOr may have zero or more child nodes. |
| type FilterOp struct { |
| Op Op |
| Exprs []Filter |
| } |
| |
| func (q *FilterOp) isFilter() {} |
| func (q *FilterOp) String() string { |
| var op string |
| switch q.Op { |
| case OpNot: |
| return fmt.Sprintf("-%s", q.Exprs[0]) |
| case OpAnd: |
| if len(q.Exprs) == 0 { |
| return "*" |
| } |
| op = " AND " |
| case OpOr: |
| if len(q.Exprs) == 0 { |
| return "-*" |
| } |
| op = " OR " |
| } |
| var buf strings.Builder |
| buf.WriteByte('(') |
| for i, e := range q.Exprs { |
| if i > 0 { |
| buf.WriteString(op) |
| } |
| buf.WriteString(e.String()) |
| } |
| buf.WriteByte(')') |
| return buf.String() |
| } |
| |
| // Op specifies a type of boolean operator. |
| type Op int |
| |
| const ( |
| OpAnd Op = 1 + iota |
| OpOr |
| OpNot |
| ) |