OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / tendermint / tmlibs / pubsub / query / query.go
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         "reflect"
14         "strconv"
15         "strings"
16         "time"
17 )
18
19 // Query holds the query string and the query parser.
20 type Query struct {
21         str    string
22         parser *QueryParser
23 }
24
25 // New parses the given string and returns a query or error if the string is
26 // invalid.
27 func New(s string) (*Query, error) {
28         p := &QueryParser{Buffer: fmt.Sprintf(`"%s"`, s)}
29         p.Init()
30         if err := p.Parse(); err != nil {
31                 return nil, err
32         }
33         return &Query{str: s, parser: p}, nil
34 }
35
36 // MustParse turns the given string into a query or panics; for tests or others
37 // cases where you know the string is valid.
38 func MustParse(s string) *Query {
39         q, err := New(s)
40         if err != nil {
41                 panic(fmt.Sprintf("failed to parse %s: %v", s, err))
42         }
43         return q
44 }
45
46 // String returns the original string.
47 func (q *Query) String() string {
48         return q.str
49 }
50
51 type operator uint8
52
53 const (
54         opLessEqual operator = iota
55         opGreaterEqual
56         opLess
57         opGreater
58         opEqual
59         opContains
60 )
61
62 // Matches returns true if the query matches the given set of tags, false otherwise.
63 //
64 // For example, query "name=John" matches tags = {"name": "John"}. More
65 // examples could be found in parser_test.go and query_test.go.
66 func (q *Query) Matches(tags map[string]interface{}) bool {
67         if len(tags) == 0 {
68                 return false
69         }
70
71         buffer, begin, end := q.parser.Buffer, 0, 0
72
73         var tag string
74         var op operator
75
76         // tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7")
77         for _, token := range q.parser.Tokens() {
78                 switch token.pegRule {
79
80                 case rulePegText:
81                         begin, end = int(token.begin), int(token.end)
82                 case ruletag:
83                         tag = buffer[begin:end]
84                 case rulele:
85                         op = opLessEqual
86                 case rulege:
87                         op = opGreaterEqual
88                 case rulel:
89                         op = opLess
90                 case ruleg:
91                         op = opGreater
92                 case ruleequal:
93                         op = opEqual
94                 case rulecontains:
95                         op = opContains
96                 case rulevalue:
97                         // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock")
98                         valueWithoutSingleQuotes := buffer[begin+1 : end-1]
99
100                         // see if the triplet (tag, operator, operand) matches any tag
101                         // "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" }
102                         if !match(tag, op, reflect.ValueOf(valueWithoutSingleQuotes), tags) {
103                                 return false
104                         }
105                 case rulenumber:
106                         number := buffer[begin:end]
107                         if strings.Contains(number, ".") { // if it looks like a floating-point number
108                                 value, err := strconv.ParseFloat(number, 64)
109                                 if err != nil {
110                                         panic(fmt.Sprintf("got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", err, number))
111                                 }
112                                 if !match(tag, op, reflect.ValueOf(value), tags) {
113                                         return false
114                                 }
115                         } else {
116                                 value, err := strconv.ParseInt(number, 10, 64)
117                                 if err != nil {
118                                         panic(fmt.Sprintf("got %v while trying to parse %s as int64 (should never happen if the grammar is correct)", err, number))
119                                 }
120                                 if !match(tag, op, reflect.ValueOf(value), tags) {
121                                         return false
122                                 }
123                         }
124                 case ruletime:
125                         value, err := time.Parse(time.RFC3339, buffer[begin:end])
126                         if err != nil {
127                                 panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", err, buffer[begin:end]))
128                         }
129                         if !match(tag, op, reflect.ValueOf(value), tags) {
130                                 return false
131                         }
132                 case ruledate:
133                         value, err := time.Parse("2006-01-02", buffer[begin:end])
134                         if err != nil {
135                                 panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / '2006-01-02' (should never happen if the grammar is correct)", err, buffer[begin:end]))
136                         }
137                         if !match(tag, op, reflect.ValueOf(value), tags) {
138                                 return false
139                         }
140                 }
141         }
142
143         return true
144 }
145
146 // match returns true if the given triplet (tag, operator, operand) matches any tag.
147 //
148 // First, it looks up the tag in tags and if it finds one, tries to compare the
149 // value from it to the operand using the operator.
150 //
151 // "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" }
152 func match(tag string, op operator, operand reflect.Value, tags map[string]interface{}) bool {
153         // look up the tag from the query in tags
154         value, ok := tags[tag]
155         if !ok {
156                 return false
157         }
158         switch operand.Kind() {
159         case reflect.Struct: // time
160                 operandAsTime := operand.Interface().(time.Time)
161                 v, ok := value.(time.Time)
162                 if !ok { // if value from tags is not time.Time
163                         return false
164                 }
165                 switch op {
166                 case opLessEqual:
167                         return v.Before(operandAsTime) || v.Equal(operandAsTime)
168                 case opGreaterEqual:
169                         return v.Equal(operandAsTime) || v.After(operandAsTime)
170                 case opLess:
171                         return v.Before(operandAsTime)
172                 case opGreater:
173                         return v.After(operandAsTime)
174                 case opEqual:
175                         return v.Equal(operandAsTime)
176                 }
177         case reflect.Float64:
178                 operandFloat64 := operand.Interface().(float64)
179                 var v float64
180                 // try our best to convert value from tags to float64
181                 switch vt := value.(type) {
182                 case float64:
183                         v = vt
184                 case float32:
185                         v = float64(vt)
186                 case int:
187                         v = float64(vt)
188                 case int8:
189                         v = float64(vt)
190                 case int16:
191                         v = float64(vt)
192                 case int32:
193                         v = float64(vt)
194                 case int64:
195                         v = float64(vt)
196                 default: // fail for all other types
197                         panic(fmt.Sprintf("Incomparable types: %T (%v) vs float64 (%v)", value, value, operandFloat64))
198                 }
199                 switch op {
200                 case opLessEqual:
201                         return v <= operandFloat64
202                 case opGreaterEqual:
203                         return v >= operandFloat64
204                 case opLess:
205                         return v < operandFloat64
206                 case opGreater:
207                         return v > operandFloat64
208                 case opEqual:
209                         return v == operandFloat64
210                 }
211         case reflect.Int64:
212                 operandInt := operand.Interface().(int64)
213                 var v int64
214                 // try our best to convert value from tags to int64
215                 switch vt := value.(type) {
216                 case int64:
217                         v = vt
218                 case int8:
219                         v = int64(vt)
220                 case int16:
221                         v = int64(vt)
222                 case int32:
223                         v = int64(vt)
224                 case int:
225                         v = int64(vt)
226                 case float64:
227                         v = int64(vt)
228                 case float32:
229                         v = int64(vt)
230                 default: // fail for all other types
231                         panic(fmt.Sprintf("Incomparable types: %T (%v) vs int64 (%v)", value, value, operandInt))
232                 }
233                 switch op {
234                 case opLessEqual:
235                         return v <= operandInt
236                 case opGreaterEqual:
237                         return v >= operandInt
238                 case opLess:
239                         return v < operandInt
240                 case opGreater:
241                         return v > operandInt
242                 case opEqual:
243                         return v == operandInt
244                 }
245         case reflect.String:
246                 v, ok := value.(string)
247                 if !ok { // if value from tags is not string
248                         return false
249                 }
250                 switch op {
251                 case opEqual:
252                         return v == operand.String()
253                 case opContains:
254                         return strings.Contains(v, operand.String())
255                 }
256         default:
257                 panic(fmt.Sprintf("Unknown kind of operand %v", operand.Kind()))
258         }
259
260         return false
261 }