| package jpath |
| |
| import ( |
| . "github.com/pelletier/go-toml" |
| ) |
| |
| type PathFn interface{ |
| Call(context interface{}, next QueryPath) |
| } |
| |
| type QueryPath []PathFn |
| |
| func (path QueryPath) Fn(context interface{}) { |
| path[0].Call(context, path[1:]) |
| } |
| |
| // shim to ease functor writing |
| func treeValue(tree *TomlTree, key string) interface{} { |
| return tree.GetPath([]string{key}) |
| } |
| |
| // match single key |
| type matchKeyFn struct { |
| Name string |
| } |
| |
| func (f *matchKeyFn) Call(context interface{}, next QueryPath) { |
| if tree, ok := context.(*TomlTree); ok { |
| item := treeValue(tree, f.Name) |
| if item != nil { |
| next.Fn(item) |
| } |
| } |
| } |
| |
| // match single index |
| type matchIndexFn struct { |
| Idx int |
| } |
| |
| func (f *matchIndexFn) Call(context interface{}, next QueryPath) { |
| if arr, ok := context.([]interface{}); ok { |
| if f.Idx < len(arr) && f.Idx >= 0 { |
| next.Fn(arr[f.Idx]) |
| } |
| } |
| } |
| |
| // filter by slicing |
| type matchSliceFn struct { |
| Start, End, Step int |
| } |
| |
| func (f *matchSliceFn) Call(context interface{}, next QueryPath) { |
| if arr, ok := context.([]interface{}); ok { |
| // adjust indexes for negative values, reverse ordering |
| realStart, realEnd := f.Start, f.End |
| if realStart < 0 { |
| realStart = len(arr) + realStart |
| } |
| if realEnd < 0 { |
| realEnd = len(arr) + realEnd |
| } |
| if realEnd < realStart { |
| realEnd, realStart = realStart, realEnd // swap |
| } |
| // loop and gather |
| for idx := realStart; idx < realEnd; idx += f.Step { |
| next.Fn(arr[idx]) |
| } |
| } |
| } |
| |
| // match anything |
| type matchAnyFn struct { |
| // empty |
| } |
| |
| func (f *matchAnyFn) Call(context interface{}, next QueryPath) { |
| if tree, ok := context.(*TomlTree); ok { |
| for _, key := range tree.Keys() { |
| item := treeValue(tree, key) |
| next.Fn(item) |
| } |
| } |
| } |
| |
| // filter through union |
| type matchUnionFn struct { |
| Union QueryPath |
| } |
| |
| func (f *matchUnionFn) Call(context interface{}, next QueryPath) { |
| for _, fn := range f.Union { |
| fn.Call(context, next) |
| } |
| } |
| |
| // match every single last node in the tree |
| type matchRecursiveFn struct { |
| // empty |
| } |
| |
| func (f *matchRecursiveFn) Call(context interface{}, next QueryPath) { |
| if tree, ok := context.(*TomlTree); ok { |
| var visit func(tree *TomlTree) |
| visit = func(tree *TomlTree) { |
| for _, key := range tree.Keys() { |
| item := treeValue(tree, key) |
| next.Fn(item) |
| switch node := item.(type) { |
| case *TomlTree: |
| visit(node) |
| case []*TomlTree: |
| for _, subtree := range node { |
| visit(subtree) |
| } |
| } |
| } |
| } |
| visit(tree) |
| } |
| } |
| |
| // terminating expression |
| type matchEndFn struct { |
| Result []interface{} |
| } |
| |
| func (f *matchEndFn) Call(context interface{}, next QueryPath) { |
| f.Result = append(f.Result, context) |
| } |
| |
| func processPath(path QueryPath, context interface{}) []interface{} { |
| // terminate the path with a collection funciton |
| endFn := &matchEndFn{ []interface{}{} } |
| newPath := append(path, endFn) |
| |
| // execute the path |
| newPath.Fn(context) |
| return endFn.Result |
| } |