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  }