github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/event/query/query.go (about) 1 // Package query provides a parser for a custom query format: 2 // 3 // abci.invoice.number=22 AND abci.invoice.owner=Ivan 4 // 5 // See query.peg for the grammar, which is a https://en.wikipedia.org/wiki/Parsing_expression_grammar. 6 // More: https://github.com/PhilippeSigaud/Pegged/wiki/PEG-Basics 7 // 8 // It has a support for numbers (integer and floating point), dates and times. 9 package query 10 11 import ( 12 "fmt" 13 ) 14 15 type Query interface { 16 Matches(tags Tagged) bool 17 String() string 18 MatchError() error 19 } 20 21 var _ Query = &PegQuery{} 22 var _ Queryable = &PegQuery{} 23 24 // Query holds the query string and the query parser. 25 type PegQuery struct { 26 str string 27 parser *QueryParser 28 error *MatchError 29 } 30 31 type MatchError struct { 32 Tagged Tagged 33 Cause error 34 } 35 36 func (m *MatchError) Error() string { 37 return fmt.Sprintf("matching error occurred with tagged: %v", m.Cause) 38 } 39 40 func IsEmpty(query Query) bool { 41 return query.String() == "" 42 } 43 44 // Condition represents a single condition within a query and consists of tag 45 // (e.g. "tx.gas"), operator (e.g. "=") and operand (e.g. "7"). 46 type Condition struct { 47 Tag string 48 Op Operator 49 Operand interface{} 50 } 51 52 // New parses the given string and returns a query or error if the string is 53 // invalid. 54 func New(s string) (*PegQuery, error) { 55 p := &QueryParser{ 56 Buffer: s, 57 } 58 p.Expression.explainer = func(format string, args ...interface{}) { 59 fmt.Printf(format, args...) 60 } 61 p.Init() 62 err := p.Parse() 63 if err != nil { 64 return nil, err 65 } 66 p.Execute() 67 return &PegQuery{str: s, parser: p}, nil 68 } 69 70 // MustParse turns the given string into a query or panics; for tests or others 71 // cases where you know the string is valid. 72 func MustParse(s string) *PegQuery { 73 q, err := New(s) 74 if err != nil { 75 panic(fmt.Sprintf("failed to parse %s: %v", s, err)) 76 } 77 return q 78 } 79 80 // String returns the original string. 81 func (q *PegQuery) String() string { 82 return q.str 83 } 84 85 func (q *PegQuery) Query() (Query, error) { 86 return q, nil 87 } 88 89 // Matches returns true if the query matches the given set of tags, false otherwise. 90 // 91 // For example, query "name=John" matches tags = {"name": "John"}. More 92 // examples could be found in parser_test.go and query_test.go. 93 func (q *PegQuery) Matches(tags Tagged) bool { 94 match, err := q.parser.Evaluate(tags.Get) 95 if err != nil { 96 q.error = &MatchError{Cause: err, Tagged: tags} 97 return false 98 } 99 return match 100 } 101 102 // Returns whether a matching error occurred (which would result in a false from Matches) 103 func (q *PegQuery) MatchError() error { 104 if q.error == nil { 105 return nil 106 } 107 return q.error 108 } 109 110 func (q *PegQuery) ExplainTo(explainer func(fmt string, args ...interface{})) { 111 q.parser.explainer = explainer 112 }