| package toml | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | ) | 
 |  | 
 | // support function to set positions for tomlValues | 
 | // NOTE: this is done to allow ctx.lastPosition to indicate the start of any | 
 | // values returned by the query engines | 
 | func tomlValueCheck(node interface{}, ctx *queryContext) interface{} { | 
 | 	switch castNode := node.(type) { | 
 | 	case *tomlValue: | 
 | 		ctx.lastPosition = castNode.position | 
 | 		return castNode.value | 
 | 	case []*TomlTree: | 
 | 		if len(castNode) > 0 { | 
 | 			ctx.lastPosition = castNode[0].position | 
 | 		} | 
 | 		return node | 
 | 	default: | 
 | 		return node | 
 | 	} | 
 | } | 
 |  | 
 | // base match | 
 | type matchBase struct { | 
 | 	next pathFn | 
 | } | 
 |  | 
 | func (f *matchBase) setNext(next pathFn) { | 
 | 	f.next = next | 
 | } | 
 |  | 
 | // terminating functor - gathers results | 
 | type terminatingFn struct { | 
 | 	// empty | 
 | } | 
 |  | 
 | func newTerminatingFn() *terminatingFn { | 
 | 	return &terminatingFn{} | 
 | } | 
 |  | 
 | func (f *terminatingFn) setNext(next pathFn) { | 
 | 	// do nothing | 
 | } | 
 |  | 
 | func (f *terminatingFn) call(node interface{}, ctx *queryContext) { | 
 | 	switch castNode := node.(type) { | 
 | 	case *TomlTree: | 
 | 		ctx.result.appendResult(node, castNode.position) | 
 | 	case *tomlValue: | 
 | 		ctx.result.appendResult(node, castNode.position) | 
 | 	default: | 
 | 		// use last position for scalars | 
 | 		ctx.result.appendResult(node, ctx.lastPosition) | 
 | 	} | 
 | } | 
 |  | 
 | // match single key | 
 | type matchKeyFn struct { | 
 | 	matchBase | 
 | 	Name string | 
 | } | 
 |  | 
 | func newMatchKeyFn(name string) *matchKeyFn { | 
 | 	return &matchKeyFn{Name: name} | 
 | } | 
 |  | 
 | func (f *matchKeyFn) call(node interface{}, ctx *queryContext) { | 
 | 	if array, ok := node.([]*TomlTree); ok { | 
 | 		for _, tree := range array { | 
 | 			item := tree.values[f.Name] | 
 | 			if item != nil { | 
 | 				f.next.call(item, ctx) | 
 | 			} | 
 | 		} | 
 | 	} else if tree, ok := node.(*TomlTree); ok { | 
 | 		item := tree.values[f.Name] | 
 | 		if item != nil { | 
 | 			f.next.call(item, ctx) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // match single index | 
 | type matchIndexFn struct { | 
 | 	matchBase | 
 | 	Idx int | 
 | } | 
 |  | 
 | func newMatchIndexFn(idx int) *matchIndexFn { | 
 | 	return &matchIndexFn{Idx: idx} | 
 | } | 
 |  | 
 | func (f *matchIndexFn) call(node interface{}, ctx *queryContext) { | 
 | 	if arr, ok := tomlValueCheck(node, ctx).([]interface{}); ok { | 
 | 		if f.Idx < len(arr) && f.Idx >= 0 { | 
 | 			f.next.call(arr[f.Idx], ctx) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // filter by slicing | 
 | type matchSliceFn struct { | 
 | 	matchBase | 
 | 	Start, End, Step int | 
 | } | 
 |  | 
 | func newMatchSliceFn(start, end, step int) *matchSliceFn { | 
 | 	return &matchSliceFn{Start: start, End: end, Step: step} | 
 | } | 
 |  | 
 | func (f *matchSliceFn) call(node interface{}, ctx *queryContext) { | 
 | 	if arr, ok := tomlValueCheck(node, ctx).([]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 { | 
 | 			f.next.call(arr[idx], ctx) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // match anything | 
 | type matchAnyFn struct { | 
 | 	matchBase | 
 | } | 
 |  | 
 | func newMatchAnyFn() *matchAnyFn { | 
 | 	return &matchAnyFn{} | 
 | } | 
 |  | 
 | func (f *matchAnyFn) call(node interface{}, ctx *queryContext) { | 
 | 	if tree, ok := node.(*TomlTree); ok { | 
 | 		for _, v := range tree.values { | 
 | 			f.next.call(v, ctx) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // filter through union | 
 | type matchUnionFn struct { | 
 | 	Union []pathFn | 
 | } | 
 |  | 
 | func (f *matchUnionFn) setNext(next pathFn) { | 
 | 	for _, fn := range f.Union { | 
 | 		fn.setNext(next) | 
 | 	} | 
 | } | 
 |  | 
 | func (f *matchUnionFn) call(node interface{}, ctx *queryContext) { | 
 | 	for _, fn := range f.Union { | 
 | 		fn.call(node, ctx) | 
 | 	} | 
 | } | 
 |  | 
 | // match every single last node in the tree | 
 | type matchRecursiveFn struct { | 
 | 	matchBase | 
 | } | 
 |  | 
 | func newMatchRecursiveFn() *matchRecursiveFn { | 
 | 	return &matchRecursiveFn{} | 
 | } | 
 |  | 
 | func (f *matchRecursiveFn) call(node interface{}, ctx *queryContext) { | 
 | 	if tree, ok := node.(*TomlTree); ok { | 
 | 		var visit func(tree *TomlTree) | 
 | 		visit = func(tree *TomlTree) { | 
 | 			for _, v := range tree.values { | 
 | 				f.next.call(v, ctx) | 
 | 				switch node := v.(type) { | 
 | 				case *TomlTree: | 
 | 					visit(node) | 
 | 				case []*TomlTree: | 
 | 					for _, subtree := range node { | 
 | 						visit(subtree) | 
 | 					} | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		f.next.call(tree, ctx) | 
 | 		visit(tree) | 
 | 	} | 
 | } | 
 |  | 
 | // match based on an externally provided functional filter | 
 | type matchFilterFn struct { | 
 | 	matchBase | 
 | 	Pos  Position | 
 | 	Name string | 
 | } | 
 |  | 
 | func newMatchFilterFn(name string, pos Position) *matchFilterFn { | 
 | 	return &matchFilterFn{Name: name, Pos: pos} | 
 | } | 
 |  | 
 | func (f *matchFilterFn) call(node interface{}, ctx *queryContext) { | 
 | 	fn, ok := (*ctx.filters)[f.Name] | 
 | 	if !ok { | 
 | 		panic(fmt.Sprintf("%s: query context does not have filter '%s'", | 
 | 			f.Pos.String(), f.Name)) | 
 | 	} | 
 | 	switch castNode := tomlValueCheck(node, ctx).(type) { | 
 | 	case *TomlTree: | 
 | 		for _, v := range castNode.values { | 
 | 			if tv, ok := v.(*tomlValue); ok { | 
 | 				if fn(tv.value) { | 
 | 					f.next.call(v, ctx) | 
 | 				} | 
 | 			} else { | 
 | 				if fn(v) { | 
 | 					f.next.call(v, ctx) | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	case []interface{}: | 
 | 		for _, v := range castNode { | 
 | 			if fn(v) { | 
 | 				f.next.call(v, ctx) | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } |