6 "github.com/pelletier/go-toml"
9 // NodeFilterFn represents a user-defined filter function, for use with
12 // The return value of the function must indicate if 'node' is to be included
13 // at this stage of the TOML path. Returning true will include the node, and
14 // returning false will exclude it.
16 // NOTE: Care should be taken to write script callbacks such that they are safe
17 // to use from multiple goroutines.
18 type NodeFilterFn func(node interface{}) bool
20 // Result is the result of Executing a Query.
23 positions []toml.Position
26 // appends a value/position pair to the result set.
27 func (r *Result) appendResult(node interface{}, pos toml.Position) {
28 r.items = append(r.items, node)
29 r.positions = append(r.positions, pos)
32 // Values is a set of values within a Result. The order of values is not
33 // guaranteed to be in document order, and may be different each time a query is
35 func (r Result) Values() []interface{} {
39 // Positions is a set of positions for values within a Result. Each index
40 // in Positions() corresponds to the entry in Value() of the same index.
41 func (r Result) Positions() []toml.Position {
45 // runtime context for executing query paths
46 type queryContext struct {
48 filters *map[string]NodeFilterFn
49 lastPosition toml.Position
52 // generic path functor interface
53 type pathFn interface {
55 // it is the caller's responsibility to set the ctx.lastPosition before invoking call()
56 // node can be one of: *toml.Tree, []*toml.Tree, or a scalar
57 call(node interface{}, ctx *queryContext)
60 // A Query is the representation of a compiled TOML path. A Query is safe
61 // for concurrent use by multiple goroutines.
65 filters *map[string]NodeFilterFn
68 func newQuery() *Query {
72 filters: &defaultFilterFunctions,
76 func (q *Query) appendPath(next pathFn) {
83 next.setNext(newTerminatingFn()) // init the next functor
86 // Compile compiles a TOML path expression. The returned Query can be used
87 // to match elements within a Tree and its descendants. See Execute.
88 func Compile(path string) (*Query, error) {
89 return parseQuery(lexQuery(path))
92 // Execute executes a query against a Tree, and returns the result of the query.
93 func (q *Query) Execute(tree *toml.Tree) *Result {
95 items: []interface{}{},
96 positions: []toml.Position{},
99 result.appendResult(tree, tree.GetPosition(""))
101 ctx := &queryContext{
105 ctx.lastPosition = tree.Position()
106 q.root.call(tree, ctx)
111 // CompileAndExecute is a shorthand for Compile(path) followed by Execute(tree).
112 func CompileAndExecute(path string, tree *toml.Tree) (*Result, error) {
113 query, err := Compile(path)
117 return query.Execute(tree), nil
120 // SetFilter sets a user-defined filter function. These may be used inside
121 // "?(..)" query expressions to filter TOML document elements within a query.
122 func (q *Query) SetFilter(name string, fn NodeFilterFn) {
123 if q.filters == &defaultFilterFunctions {
124 // clone the static table
125 q.filters = &map[string]NodeFilterFn{}
126 for k, v := range defaultFilterFunctions {
130 (*q.filters)[name] = fn
133 var defaultFilterFunctions = map[string]NodeFilterFn{
134 "tree": func(node interface{}) bool {
135 _, ok := node.(*toml.Tree)
138 "int": func(node interface{}) bool {
139 _, ok := node.(int64)
142 "float": func(node interface{}) bool {
143 _, ok := node.(float64)
146 "string": func(node interface{}) bool {
147 _, ok := node.(string)
150 "time": func(node interface{}) bool {
151 _, ok := node.(time.Time)
154 "bool": func(node interface{}) bool {