...
1 package query
2
3 import (
4 "time"
5
6 "github.com/pelletier/go-toml"
7 )
8
9
10
11
12
13
14
15
16
17
18 type NodeFilterFn func(node interface{}) bool
19
20
21 type Result struct {
22 items []interface{}
23 positions []toml.Position
24 }
25
26
27 func (r *Result) appendResult(node interface{}, pos toml.Position) {
28 r.items = append(r.items, node)
29 r.positions = append(r.positions, pos)
30 }
31
32
33
34
35 func (r Result) Values() []interface{} {
36 return r.items
37 }
38
39
40
41 func (r Result) Positions() []toml.Position {
42 return r.positions
43 }
44
45
46 type queryContext struct {
47 result *Result
48 filters *map[string]NodeFilterFn
49 lastPosition toml.Position
50 }
51
52
53 type pathFn interface {
54 setNext(next pathFn)
55
56
57 call(node interface{}, ctx *queryContext)
58 }
59
60
61
62 type Query struct {
63 root pathFn
64 tail pathFn
65 filters *map[string]NodeFilterFn
66 }
67
68 func newQuery() *Query {
69 return &Query{
70 root: nil,
71 tail: nil,
72 filters: &defaultFilterFunctions,
73 }
74 }
75
76 func (q *Query) appendPath(next pathFn) {
77 if q.root == nil {
78 q.root = next
79 } else {
80 q.tail.setNext(next)
81 }
82 q.tail = next
83 next.setNext(newTerminatingFn())
84 }
85
86
87
88 func Compile(path string) (*Query, error) {
89 return parseQuery(lexQuery(path))
90 }
91
92
93 func (q *Query) Execute(tree *toml.Tree) *Result {
94 result := &Result{
95 items: []interface{}{},
96 positions: []toml.Position{},
97 }
98 if q.root == nil {
99 result.appendResult(tree, tree.GetPosition(""))
100 } else {
101 ctx := &queryContext{
102 result: result,
103 filters: q.filters,
104 }
105 ctx.lastPosition = tree.Position()
106 q.root.call(tree, ctx)
107 }
108 return result
109 }
110
111
112 func CompileAndExecute(path string, tree *toml.Tree) (*Result, error) {
113 query, err := Compile(path)
114 if err != nil {
115 return nil, err
116 }
117 return query.Execute(tree), nil
118 }
119
120
121
122 func (q *Query) SetFilter(name string, fn NodeFilterFn) {
123 if q.filters == &defaultFilterFunctions {
124
125 q.filters = &map[string]NodeFilterFn{}
126 for k, v := range defaultFilterFunctions {
127 (*q.filters)[k] = v
128 }
129 }
130 (*q.filters)[name] = fn
131 }
132
133 var defaultFilterFunctions = map[string]NodeFilterFn{
134 "tree": func(node interface{}) bool {
135 _, ok := node.(*toml.Tree)
136 return ok
137 },
138 "int": func(node interface{}) bool {
139 _, ok := node.(int64)
140 return ok
141 },
142 "float": func(node interface{}) bool {
143 _, ok := node.(float64)
144 return ok
145 },
146 "string": func(node interface{}) bool {
147 _, ok := node.(string)
148 return ok
149 },
150 "time": func(node interface{}) bool {
151 _, ok := node.(time.Time)
152 return ok
153 },
154 "bool": func(node interface{}) bool {
155 _, ok := node.(bool)
156 return ok
157 },
158 }
159
View as plain text