|  | package toml | 
|  |  | 
|  | import ( | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | // NodeFilterFn represents a user-defined filter function, for use with | 
|  | // Query.SetFilter(). | 
|  | // | 
|  | // The return value of the function must indicate if 'node' is to be included | 
|  | // at this stage of the TOML path.  Returning true will include the node, and | 
|  | // returning false will exclude it. | 
|  | // | 
|  | // NOTE: Care should be taken to write script callbacks such that they are safe | 
|  | // to use from multiple goroutines. | 
|  | type NodeFilterFn func(node interface{}) bool | 
|  |  | 
|  | // QueryResult is the result of Executing a Query. | 
|  | type QueryResult struct { | 
|  | items     []interface{} | 
|  | positions []Position | 
|  | } | 
|  |  | 
|  | // appends a value/position pair to the result set. | 
|  | func (r *QueryResult) appendResult(node interface{}, pos Position) { | 
|  | r.items = append(r.items, node) | 
|  | r.positions = append(r.positions, pos) | 
|  | } | 
|  |  | 
|  | // Values is a set of values within a QueryResult.  The order of values is not | 
|  | // guaranteed to be in document order, and may be different each time a query is | 
|  | // executed. | 
|  | func (r QueryResult) Values() []interface{} { | 
|  | values := make([]interface{}, len(r.items)) | 
|  | for i, v := range r.items { | 
|  | o, ok := v.(*tomlValue) | 
|  | if ok { | 
|  | values[i] = o.value | 
|  | } else { | 
|  | values[i] = v | 
|  | } | 
|  | } | 
|  | return values | 
|  | } | 
|  |  | 
|  | // Positions is a set of positions for values within a QueryResult.  Each index | 
|  | // in Positions() corresponds to the entry in Value() of the same index. | 
|  | func (r QueryResult) Positions() []Position { | 
|  | return r.positions | 
|  | } | 
|  |  | 
|  | // runtime context for executing query paths | 
|  | type queryContext struct { | 
|  | result       *QueryResult | 
|  | filters      *map[string]NodeFilterFn | 
|  | lastPosition Position | 
|  | } | 
|  |  | 
|  | // generic path functor interface | 
|  | type pathFn interface { | 
|  | setNext(next pathFn) | 
|  | call(node interface{}, ctx *queryContext) | 
|  | } | 
|  |  | 
|  | // A Query is the representation of a compiled TOML path.  A Query is safe | 
|  | // for concurrent use by multiple goroutines. | 
|  | type Query struct { | 
|  | root    pathFn | 
|  | tail    pathFn | 
|  | filters *map[string]NodeFilterFn | 
|  | } | 
|  |  | 
|  | func newQuery() *Query { | 
|  | return &Query{ | 
|  | root:    nil, | 
|  | tail:    nil, | 
|  | filters: &defaultFilterFunctions, | 
|  | } | 
|  | } | 
|  |  | 
|  | func (q *Query) appendPath(next pathFn) { | 
|  | if q.root == nil { | 
|  | q.root = next | 
|  | } else { | 
|  | q.tail.setNext(next) | 
|  | } | 
|  | q.tail = next | 
|  | next.setNext(newTerminatingFn()) // init the next functor | 
|  | } | 
|  |  | 
|  | // CompileQuery compiles a TOML path expression.  The returned Query can be used | 
|  | // to match elements within a TomlTree and its descendants. | 
|  | func CompileQuery(path string) (*Query, error) { | 
|  | return parseQuery(lexQuery(path)) | 
|  | } | 
|  |  | 
|  | // Execute executes a query against a TomlTree, and returns the result of the query. | 
|  | func (q *Query) Execute(tree *TomlTree) *QueryResult { | 
|  | result := &QueryResult{ | 
|  | items:     []interface{}{}, | 
|  | positions: []Position{}, | 
|  | } | 
|  | if q.root == nil { | 
|  | result.appendResult(tree, tree.GetPosition("")) | 
|  | } else { | 
|  | ctx := &queryContext{ | 
|  | result:  result, | 
|  | filters: q.filters, | 
|  | } | 
|  | q.root.call(tree, ctx) | 
|  | } | 
|  | return result | 
|  | } | 
|  |  | 
|  | // SetFilter sets a user-defined filter function.  These may be used inside | 
|  | // "?(..)" query expressions to filter TOML document elements within a query. | 
|  | func (q *Query) SetFilter(name string, fn NodeFilterFn) { | 
|  | if q.filters == &defaultFilterFunctions { | 
|  | // clone the static table | 
|  | q.filters = &map[string]NodeFilterFn{} | 
|  | for k, v := range defaultFilterFunctions { | 
|  | (*q.filters)[k] = v | 
|  | } | 
|  | } | 
|  | (*q.filters)[name] = fn | 
|  | } | 
|  |  | 
|  | var defaultFilterFunctions = map[string]NodeFilterFn{ | 
|  | "tree": func(node interface{}) bool { | 
|  | _, ok := node.(*TomlTree) | 
|  | return ok | 
|  | }, | 
|  | "int": func(node interface{}) bool { | 
|  | _, ok := node.(int64) | 
|  | return ok | 
|  | }, | 
|  | "float": func(node interface{}) bool { | 
|  | _, ok := node.(float64) | 
|  | return ok | 
|  | }, | 
|  | "string": func(node interface{}) bool { | 
|  | _, ok := node.(string) | 
|  | return ok | 
|  | }, | 
|  | "time": func(node interface{}) bool { | 
|  | _, ok := node.(time.Time) | 
|  | return ok | 
|  | }, | 
|  | "bool": func(node interface{}) bool { | 
|  | _, ok := node.(bool) | 
|  | return ok | 
|  | }, | 
|  | } |