1 package query
2
3 import (
4 "fmt"
5 "reflect"
6
7 "github.com/pelletier/go-toml"
8 )
9
10
11 type matchBase struct {
12 next pathFn
13 }
14
15 func (f *matchBase) setNext(next pathFn) {
16 f.next = next
17 }
18
19
20 type terminatingFn struct {
21
22 }
23
24 func newTerminatingFn() *terminatingFn {
25 return &terminatingFn{}
26 }
27
28 func (f *terminatingFn) setNext(next pathFn) {
29
30 }
31
32 func (f *terminatingFn) call(node interface{}, ctx *queryContext) {
33 ctx.result.appendResult(node, ctx.lastPosition)
34 }
35
36
37 type matchKeyFn struct {
38 matchBase
39 Name string
40 }
41
42 func newMatchKeyFn(name string) *matchKeyFn {
43 return &matchKeyFn{Name: name}
44 }
45
46 func (f *matchKeyFn) call(node interface{}, ctx *queryContext) {
47 if array, ok := node.([]*toml.Tree); ok {
48 for _, tree := range array {
49 item := tree.GetPath([]string{f.Name})
50 if item != nil {
51 ctx.lastPosition = tree.GetPositionPath([]string{f.Name})
52 f.next.call(item, ctx)
53 }
54 }
55 } else if tree, ok := node.(*toml.Tree); ok {
56 item := tree.GetPath([]string{f.Name})
57 if item != nil {
58 ctx.lastPosition = tree.GetPositionPath([]string{f.Name})
59 f.next.call(item, ctx)
60 }
61 }
62 }
63
64
65 type matchIndexFn struct {
66 matchBase
67 Idx int
68 }
69
70 func newMatchIndexFn(idx int) *matchIndexFn {
71 return &matchIndexFn{Idx: idx}
72 }
73
74 func (f *matchIndexFn) call(node interface{}, ctx *queryContext) {
75 v := reflect.ValueOf(node)
76 if v.Kind() == reflect.Slice {
77 if v.Len() == 0 {
78 return
79 }
80
81
82 idx := f.Idx
83 if idx < 0 {
84 idx += v.Len()
85 }
86 if 0 <= idx && idx < v.Len() {
87 callNextIndexSlice(f.next, node, ctx, v.Index(idx).Interface())
88 }
89 }
90 }
91
92 func callNextIndexSlice(next pathFn, node interface{}, ctx *queryContext, value interface{}) {
93 if treesArray, ok := node.([]*toml.Tree); ok {
94 ctx.lastPosition = treesArray[0].Position()
95 }
96 next.call(value, ctx)
97 }
98
99
100 type matchSliceFn struct {
101 matchBase
102 Start, End, Step *int
103 }
104
105 func newMatchSliceFn() *matchSliceFn {
106 return &matchSliceFn{}
107 }
108
109 func (f *matchSliceFn) setStart(start int) *matchSliceFn {
110 f.Start = &start
111 return f
112 }
113
114 func (f *matchSliceFn) setEnd(end int) *matchSliceFn {
115 f.End = &end
116 return f
117 }
118
119 func (f *matchSliceFn) setStep(step int) *matchSliceFn {
120 f.Step = &step
121 return f
122 }
123
124 func (f *matchSliceFn) call(node interface{}, ctx *queryContext) {
125 v := reflect.ValueOf(node)
126 if v.Kind() == reflect.Slice {
127 if v.Len() == 0 {
128 return
129 }
130
131 var start, end, step int
132
133
134 if f.Step != nil {
135 step = *f.Step
136 } else {
137 step = 1
138 }
139
140
141 if f.Start != nil {
142 start = *f.Start
143
144 if start < 0 {
145 start += v.Len()
146 }
147
148 start = max(start, 0)
149 start = min(start, v.Len()-1)
150 } else if step > 0 {
151 start = 0
152 } else {
153 start = v.Len() - 1
154 }
155
156
157 if f.End != nil {
158 end = *f.End
159
160 if end < 0 {
161 end += v.Len()
162 }
163
164 end = max(end, -1)
165 end = min(end, v.Len())
166 } else if step > 0 {
167 end = v.Len()
168 } else {
169 end = -1
170 }
171
172
173 if step > 0 {
174 for idx := start; idx < end; idx += step {
175 callNextIndexSlice(f.next, node, ctx, v.Index(idx).Interface())
176 }
177 } else {
178 for idx := start; idx > end; idx += step {
179 callNextIndexSlice(f.next, node, ctx, v.Index(idx).Interface())
180 }
181 }
182 }
183 }
184
185 func min(a, b int) int {
186 if a < b {
187 return a
188 }
189 return b
190 }
191
192 func max(a, b int) int {
193 if a > b {
194 return a
195 }
196 return b
197 }
198
199
200 type matchAnyFn struct {
201 matchBase
202 }
203
204 func newMatchAnyFn() *matchAnyFn {
205 return &matchAnyFn{}
206 }
207
208 func (f *matchAnyFn) call(node interface{}, ctx *queryContext) {
209 if tree, ok := node.(*toml.Tree); ok {
210 for _, k := range tree.Keys() {
211 v := tree.GetPath([]string{k})
212 ctx.lastPosition = tree.GetPositionPath([]string{k})
213 f.next.call(v, ctx)
214 }
215 }
216 }
217
218
219 type matchUnionFn struct {
220 Union []pathFn
221 }
222
223 func (f *matchUnionFn) setNext(next pathFn) {
224 for _, fn := range f.Union {
225 fn.setNext(next)
226 }
227 }
228
229 func (f *matchUnionFn) call(node interface{}, ctx *queryContext) {
230 for _, fn := range f.Union {
231 fn.call(node, ctx)
232 }
233 }
234
235
236 type matchRecursiveFn struct {
237 matchBase
238 }
239
240 func newMatchRecursiveFn() *matchRecursiveFn {
241 return &matchRecursiveFn{}
242 }
243
244 func (f *matchRecursiveFn) call(node interface{}, ctx *queryContext) {
245 originalPosition := ctx.lastPosition
246 if tree, ok := node.(*toml.Tree); ok {
247 var visit func(tree *toml.Tree)
248 visit = func(tree *toml.Tree) {
249 for _, k := range tree.Keys() {
250 v := tree.GetPath([]string{k})
251 ctx.lastPosition = tree.GetPositionPath([]string{k})
252 f.next.call(v, ctx)
253 switch node := v.(type) {
254 case *toml.Tree:
255 visit(node)
256 case []*toml.Tree:
257 for _, subtree := range node {
258 visit(subtree)
259 }
260 }
261 }
262 }
263 ctx.lastPosition = originalPosition
264 f.next.call(tree, ctx)
265 visit(tree)
266 }
267 }
268
269
270 type matchFilterFn struct {
271 matchBase
272 Pos toml.Position
273 Name string
274 }
275
276 func newMatchFilterFn(name string, pos toml.Position) *matchFilterFn {
277 return &matchFilterFn{Name: name, Pos: pos}
278 }
279
280 func (f *matchFilterFn) call(node interface{}, ctx *queryContext) {
281 fn, ok := (*ctx.filters)[f.Name]
282 if !ok {
283 panic(fmt.Sprintf("%s: query context does not have filter '%s'",
284 f.Pos.String(), f.Name))
285 }
286 switch castNode := node.(type) {
287 case *toml.Tree:
288 for _, k := range castNode.Keys() {
289 v := castNode.GetPath([]string{k})
290 if fn(v) {
291 ctx.lastPosition = castNode.GetPositionPath([]string{k})
292 f.next.call(v, ctx)
293 }
294 }
295 case []*toml.Tree:
296 for _, v := range castNode {
297 if fn(v) {
298 if len(castNode) > 0 {
299 ctx.lastPosition = castNode[0].Position()
300 }
301 f.next.call(v, ctx)
302 }
303 }
304 case []interface{}:
305 for _, v := range castNode {
306 if fn(v) {
307 f.next.call(v, ctx)
308 }
309 }
310 }
311 }
312
View as plain text