mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 02:03:20 -04:00 
			
		
		
		
	Add route53 plugin (#1390)
* Update vendor Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Add route53 plugin This fix adds route53 plugin so that it is possible to query route53 record through CoreDNS. Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
		
							
								
								
									
										4
									
								
								vendor/github.com/jmespath/go-jmespath/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/jmespath/go-jmespath/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| jpgo | ||||
| jmespath-fuzz.zip | ||||
| cpu.out | ||||
| go-jmespath.test | ||||
							
								
								
									
										9
									
								
								vendor/github.com/jmespath/go-jmespath/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/jmespath/go-jmespath/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| language: go | ||||
|  | ||||
| sudo: false | ||||
|  | ||||
| go: | ||||
|   - 1.4 | ||||
|  | ||||
| install: go get -v -t ./... | ||||
| script: make test | ||||
							
								
								
									
										13
									
								
								vendor/github.com/jmespath/go-jmespath/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/jmespath/go-jmespath/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| Copyright 2015 James Saryerwinnie | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
							
								
								
									
										44
									
								
								vendor/github.com/jmespath/go-jmespath/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								vendor/github.com/jmespath/go-jmespath/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
|  | ||||
| CMD = jpgo | ||||
|  | ||||
| help: | ||||
| 	@echo "Please use \`make <target>' where <target> is one of" | ||||
| 	@echo "  test                    to run all the tests" | ||||
| 	@echo "  build                   to build the library and jp executable" | ||||
| 	@echo "  generate                to run codegen" | ||||
|  | ||||
|  | ||||
| generate: | ||||
| 	go generate ./... | ||||
|  | ||||
| build: | ||||
| 	rm -f $(CMD) | ||||
| 	go build ./... | ||||
| 	rm -f cmd/$(CMD)/$(CMD) && cd cmd/$(CMD)/ && go build ./... | ||||
| 	mv cmd/$(CMD)/$(CMD) . | ||||
|  | ||||
| test: | ||||
| 	go test -v ./... | ||||
|  | ||||
| check: | ||||
| 	go vet ./... | ||||
| 	@echo "golint ./..." | ||||
| 	@lint=`golint ./...`; \ | ||||
| 	lint=`echo "$$lint" | grep -v "astnodetype_string.go" | grep -v "toktype_string.go"`; \ | ||||
| 	echo "$$lint"; \ | ||||
| 	if [ "$$lint" != "" ]; then exit 1; fi | ||||
|  | ||||
| htmlc: | ||||
| 	go test -coverprofile="/tmp/jpcov"  && go tool cover -html="/tmp/jpcov" && unlink /tmp/jpcov | ||||
|  | ||||
| buildfuzz: | ||||
| 	go-fuzz-build github.com/jmespath/go-jmespath/fuzz | ||||
|  | ||||
| fuzz: buildfuzz | ||||
| 	go-fuzz -bin=./jmespath-fuzz.zip -workdir=fuzz/testdata | ||||
|  | ||||
| bench: | ||||
| 	go test -bench . -cpuprofile cpu.out | ||||
|  | ||||
| pprof-cpu: | ||||
| 	go tool pprof ./go-jmespath.test ./cpu.out | ||||
							
								
								
									
										7
									
								
								vendor/github.com/jmespath/go-jmespath/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/jmespath/go-jmespath/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| # go-jmespath - A JMESPath implementation in Go | ||||
|  | ||||
| [](https://travis-ci.org/jmespath/go-jmespath) | ||||
|  | ||||
|  | ||||
|  | ||||
| See http://jmespath.org for more info. | ||||
							
								
								
									
										49
									
								
								vendor/github.com/jmespath/go-jmespath/api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/jmespath/go-jmespath/api.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| package jmespath | ||||
|  | ||||
| import "strconv" | ||||
|  | ||||
| // JmesPath is the epresentation of a compiled JMES path query. A JmesPath is | ||||
| // safe for concurrent use by multiple goroutines. | ||||
| type JMESPath struct { | ||||
| 	ast  ASTNode | ||||
| 	intr *treeInterpreter | ||||
| } | ||||
|  | ||||
| // Compile parses a JMESPath expression and returns, if successful, a JMESPath | ||||
| // object that can be used to match against data. | ||||
| func Compile(expression string) (*JMESPath, error) { | ||||
| 	parser := NewParser() | ||||
| 	ast, err := parser.Parse(expression) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	jmespath := &JMESPath{ast: ast, intr: newInterpreter()} | ||||
| 	return jmespath, nil | ||||
| } | ||||
|  | ||||
| // MustCompile is like Compile but panics if the expression cannot be parsed. | ||||
| // It simplifies safe initialization of global variables holding compiled | ||||
| // JMESPaths. | ||||
| func MustCompile(expression string) *JMESPath { | ||||
| 	jmespath, err := Compile(expression) | ||||
| 	if err != nil { | ||||
| 		panic(`jmespath: Compile(` + strconv.Quote(expression) + `): ` + err.Error()) | ||||
| 	} | ||||
| 	return jmespath | ||||
| } | ||||
|  | ||||
| // Search evaluates a JMESPath expression against input data and returns the result. | ||||
| func (jp *JMESPath) Search(data interface{}) (interface{}, error) { | ||||
| 	return jp.intr.Execute(jp.ast, data) | ||||
| } | ||||
|  | ||||
| // Search evaluates a JMESPath expression against input data and returns the result. | ||||
| func Search(expression string, data interface{}) (interface{}, error) { | ||||
| 	intr := newInterpreter() | ||||
| 	parser := NewParser() | ||||
| 	ast, err := parser.Parse(expression) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return intr.Execute(ast, data) | ||||
| } | ||||
							
								
								
									
										32
									
								
								vendor/github.com/jmespath/go-jmespath/api_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/jmespath/go-jmespath/api_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestValidPrecompiledExpressionSearches(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := make(map[string]interface{}) | ||||
| 	data["foo"] = "bar" | ||||
| 	precompiled, err := Compile("foo") | ||||
| 	assert.Nil(err) | ||||
| 	result, err := precompiled.Search(data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal("bar", result) | ||||
| } | ||||
|  | ||||
| func TestInvalidPrecompileErrors(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	_, err := Compile("not a valid expression") | ||||
| 	assert.NotNil(err) | ||||
| } | ||||
|  | ||||
| func TestInvalidMustCompilePanics(t *testing.T) { | ||||
| 	defer func() { | ||||
| 		r := recover() | ||||
| 		assert.NotNil(t, r) | ||||
| 	}() | ||||
| 	MustCompile("not a valid expression") | ||||
| } | ||||
							
								
								
									
										16
									
								
								vendor/github.com/jmespath/go-jmespath/astnodetype_string.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/jmespath/go-jmespath/astnodetype_string.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| // generated by stringer -type astNodeType; DO NOT EDIT | ||||
|  | ||||
| package jmespath | ||||
|  | ||||
| import "fmt" | ||||
|  | ||||
| const _astNodeType_name = "ASTEmptyASTComparatorASTCurrentNodeASTExpRefASTFunctionExpressionASTFieldASTFilterProjectionASTFlattenASTIdentityASTIndexASTIndexExpressionASTKeyValPairASTLiteralASTMultiSelectHashASTMultiSelectListASTOrExpressionASTAndExpressionASTNotExpressionASTPipeASTProjectionASTSubexpressionASTSliceASTValueProjection" | ||||
|  | ||||
| var _astNodeType_index = [...]uint16{0, 8, 21, 35, 44, 65, 73, 92, 102, 113, 121, 139, 152, 162, 180, 198, 213, 229, 245, 252, 265, 281, 289, 307} | ||||
|  | ||||
| func (i astNodeType) String() string { | ||||
| 	if i < 0 || i >= astNodeType(len(_astNodeType_index)-1) { | ||||
| 		return fmt.Sprintf("astNodeType(%d)", i) | ||||
| 	} | ||||
| 	return _astNodeType_name[_astNodeType_index[i]:_astNodeType_index[i+1]] | ||||
| } | ||||
							
								
								
									
										123
									
								
								vendor/github.com/jmespath/go-jmespath/compliance_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/jmespath/go-jmespath/compliance_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| type TestSuite struct { | ||||
| 	Given     interface{} | ||||
| 	TestCases []TestCase `json:"cases"` | ||||
| 	Comment   string | ||||
| } | ||||
| type TestCase struct { | ||||
| 	Comment    string | ||||
| 	Expression string | ||||
| 	Result     interface{} | ||||
| 	Error      string | ||||
| } | ||||
|  | ||||
| var whiteListed = []string{ | ||||
| 	"compliance/basic.json", | ||||
| 	"compliance/current.json", | ||||
| 	"compliance/escape.json", | ||||
| 	"compliance/filters.json", | ||||
| 	"compliance/functions.json", | ||||
| 	"compliance/identifiers.json", | ||||
| 	"compliance/indices.json", | ||||
| 	"compliance/literal.json", | ||||
| 	"compliance/multiselect.json", | ||||
| 	"compliance/ormatch.json", | ||||
| 	"compliance/pipe.json", | ||||
| 	"compliance/slice.json", | ||||
| 	"compliance/syntax.json", | ||||
| 	"compliance/unicode.json", | ||||
| 	"compliance/wildcard.json", | ||||
| 	"compliance/boolean.json", | ||||
| } | ||||
|  | ||||
| func allowed(path string) bool { | ||||
| 	for _, el := range whiteListed { | ||||
| 		if el == path { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func TestCompliance(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
|  | ||||
| 	var complianceFiles []string | ||||
| 	err := filepath.Walk("compliance", func(path string, _ os.FileInfo, _ error) error { | ||||
| 		//if strings.HasSuffix(path, ".json") { | ||||
| 		if allowed(path) { | ||||
| 			complianceFiles = append(complianceFiles, path) | ||||
| 		} | ||||
| 		return nil | ||||
| 	}) | ||||
| 	if assert.Nil(err) { | ||||
| 		for _, filename := range complianceFiles { | ||||
| 			runComplianceTest(assert, filename) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runComplianceTest(assert *assert.Assertions, filename string) { | ||||
| 	var testSuites []TestSuite | ||||
| 	data, err := ioutil.ReadFile(filename) | ||||
| 	if assert.Nil(err) { | ||||
| 		err := json.Unmarshal(data, &testSuites) | ||||
| 		if assert.Nil(err) { | ||||
| 			for _, testsuite := range testSuites { | ||||
| 				runTestSuite(assert, testsuite, filename) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runTestSuite(assert *assert.Assertions, testsuite TestSuite, filename string) { | ||||
| 	for _, testcase := range testsuite.TestCases { | ||||
| 		if testcase.Error != "" { | ||||
| 			// This is a test case that verifies we error out properly. | ||||
| 			runSyntaxTestCase(assert, testsuite.Given, testcase, filename) | ||||
| 		} else { | ||||
| 			runTestCase(assert, testsuite.Given, testcase, filename) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runSyntaxTestCase(assert *assert.Assertions, given interface{}, testcase TestCase, filename string) { | ||||
| 	// Anything with an .Error means that we expect that JMESPath should return | ||||
| 	// an error when we try to evaluate the expression. | ||||
| 	_, err := Search(testcase.Expression, given) | ||||
| 	assert.NotNil(err, fmt.Sprintf("Expression: %s", testcase.Expression)) | ||||
| } | ||||
|  | ||||
| func runTestCase(assert *assert.Assertions, given interface{}, testcase TestCase, filename string) { | ||||
| 	lexer := NewLexer() | ||||
| 	var err error | ||||
| 	_, err = lexer.tokenize(testcase.Expression) | ||||
| 	if err != nil { | ||||
| 		errMsg := fmt.Sprintf("(%s) Could not lex expression: %s -- %s", filename, testcase.Expression, err.Error()) | ||||
| 		assert.Fail(errMsg) | ||||
| 		return | ||||
| 	} | ||||
| 	parser := NewParser() | ||||
| 	_, err = parser.Parse(testcase.Expression) | ||||
| 	if err != nil { | ||||
| 		errMsg := fmt.Sprintf("(%s) Could not parse expression: %s -- %s", filename, testcase.Expression, err.Error()) | ||||
| 		assert.Fail(errMsg) | ||||
| 		return | ||||
| 	} | ||||
| 	actual, err := Search(testcase.Expression, given) | ||||
| 	if assert.Nil(err, fmt.Sprintf("Expression: %s", testcase.Expression)) { | ||||
| 		assert.Equal(testcase.Result, actual, fmt.Sprintf("Expression: %s", testcase.Expression)) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										842
									
								
								vendor/github.com/jmespath/go-jmespath/functions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										842
									
								
								vendor/github.com/jmespath/go-jmespath/functions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,842 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"reflect" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| type jpFunction func(arguments []interface{}) (interface{}, error) | ||||
|  | ||||
| type jpType string | ||||
|  | ||||
| const ( | ||||
| 	jpUnknown     jpType = "unknown" | ||||
| 	jpNumber      jpType = "number" | ||||
| 	jpString      jpType = "string" | ||||
| 	jpArray       jpType = "array" | ||||
| 	jpObject      jpType = "object" | ||||
| 	jpArrayNumber jpType = "array[number]" | ||||
| 	jpArrayString jpType = "array[string]" | ||||
| 	jpExpref      jpType = "expref" | ||||
| 	jpAny         jpType = "any" | ||||
| ) | ||||
|  | ||||
| type functionEntry struct { | ||||
| 	name      string | ||||
| 	arguments []argSpec | ||||
| 	handler   jpFunction | ||||
| 	hasExpRef bool | ||||
| } | ||||
|  | ||||
| type argSpec struct { | ||||
| 	types    []jpType | ||||
| 	variadic bool | ||||
| } | ||||
|  | ||||
| type byExprString struct { | ||||
| 	intr     *treeInterpreter | ||||
| 	node     ASTNode | ||||
| 	items    []interface{} | ||||
| 	hasError bool | ||||
| } | ||||
|  | ||||
| func (a *byExprString) Len() int { | ||||
| 	return len(a.items) | ||||
| } | ||||
| func (a *byExprString) Swap(i, j int) { | ||||
| 	a.items[i], a.items[j] = a.items[j], a.items[i] | ||||
| } | ||||
| func (a *byExprString) Less(i, j int) bool { | ||||
| 	first, err := a.intr.Execute(a.node, a.items[i]) | ||||
| 	if err != nil { | ||||
| 		a.hasError = true | ||||
| 		// Return a dummy value. | ||||
| 		return true | ||||
| 	} | ||||
| 	ith, ok := first.(string) | ||||
| 	if !ok { | ||||
| 		a.hasError = true | ||||
| 		return true | ||||
| 	} | ||||
| 	second, err := a.intr.Execute(a.node, a.items[j]) | ||||
| 	if err != nil { | ||||
| 		a.hasError = true | ||||
| 		// Return a dummy value. | ||||
| 		return true | ||||
| 	} | ||||
| 	jth, ok := second.(string) | ||||
| 	if !ok { | ||||
| 		a.hasError = true | ||||
| 		return true | ||||
| 	} | ||||
| 	return ith < jth | ||||
| } | ||||
|  | ||||
| type byExprFloat struct { | ||||
| 	intr     *treeInterpreter | ||||
| 	node     ASTNode | ||||
| 	items    []interface{} | ||||
| 	hasError bool | ||||
| } | ||||
|  | ||||
| func (a *byExprFloat) Len() int { | ||||
| 	return len(a.items) | ||||
| } | ||||
| func (a *byExprFloat) Swap(i, j int) { | ||||
| 	a.items[i], a.items[j] = a.items[j], a.items[i] | ||||
| } | ||||
| func (a *byExprFloat) Less(i, j int) bool { | ||||
| 	first, err := a.intr.Execute(a.node, a.items[i]) | ||||
| 	if err != nil { | ||||
| 		a.hasError = true | ||||
| 		// Return a dummy value. | ||||
| 		return true | ||||
| 	} | ||||
| 	ith, ok := first.(float64) | ||||
| 	if !ok { | ||||
| 		a.hasError = true | ||||
| 		return true | ||||
| 	} | ||||
| 	second, err := a.intr.Execute(a.node, a.items[j]) | ||||
| 	if err != nil { | ||||
| 		a.hasError = true | ||||
| 		// Return a dummy value. | ||||
| 		return true | ||||
| 	} | ||||
| 	jth, ok := second.(float64) | ||||
| 	if !ok { | ||||
| 		a.hasError = true | ||||
| 		return true | ||||
| 	} | ||||
| 	return ith < jth | ||||
| } | ||||
|  | ||||
| type functionCaller struct { | ||||
| 	functionTable map[string]functionEntry | ||||
| } | ||||
|  | ||||
| func newFunctionCaller() *functionCaller { | ||||
| 	caller := &functionCaller{} | ||||
| 	caller.functionTable = map[string]functionEntry{ | ||||
| 		"length": { | ||||
| 			name: "length", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpString, jpArray, jpObject}}, | ||||
| 			}, | ||||
| 			handler: jpfLength, | ||||
| 		}, | ||||
| 		"starts_with": { | ||||
| 			name: "starts_with", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpString}}, | ||||
| 				{types: []jpType{jpString}}, | ||||
| 			}, | ||||
| 			handler: jpfStartsWith, | ||||
| 		}, | ||||
| 		"abs": { | ||||
| 			name: "abs", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpNumber}}, | ||||
| 			}, | ||||
| 			handler: jpfAbs, | ||||
| 		}, | ||||
| 		"avg": { | ||||
| 			name: "avg", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpArrayNumber}}, | ||||
| 			}, | ||||
| 			handler: jpfAvg, | ||||
| 		}, | ||||
| 		"ceil": { | ||||
| 			name: "ceil", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpNumber}}, | ||||
| 			}, | ||||
| 			handler: jpfCeil, | ||||
| 		}, | ||||
| 		"contains": { | ||||
| 			name: "contains", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpArray, jpString}}, | ||||
| 				{types: []jpType{jpAny}}, | ||||
| 			}, | ||||
| 			handler: jpfContains, | ||||
| 		}, | ||||
| 		"ends_with": { | ||||
| 			name: "ends_with", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpString}}, | ||||
| 				{types: []jpType{jpString}}, | ||||
| 			}, | ||||
| 			handler: jpfEndsWith, | ||||
| 		}, | ||||
| 		"floor": { | ||||
| 			name: "floor", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpNumber}}, | ||||
| 			}, | ||||
| 			handler: jpfFloor, | ||||
| 		}, | ||||
| 		"map": { | ||||
| 			name: "amp", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpExpref}}, | ||||
| 				{types: []jpType{jpArray}}, | ||||
| 			}, | ||||
| 			handler:   jpfMap, | ||||
| 			hasExpRef: true, | ||||
| 		}, | ||||
| 		"max": { | ||||
| 			name: "max", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpArrayNumber, jpArrayString}}, | ||||
| 			}, | ||||
| 			handler: jpfMax, | ||||
| 		}, | ||||
| 		"merge": { | ||||
| 			name: "merge", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpObject}, variadic: true}, | ||||
| 			}, | ||||
| 			handler: jpfMerge, | ||||
| 		}, | ||||
| 		"max_by": { | ||||
| 			name: "max_by", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpArray}}, | ||||
| 				{types: []jpType{jpExpref}}, | ||||
| 			}, | ||||
| 			handler:   jpfMaxBy, | ||||
| 			hasExpRef: true, | ||||
| 		}, | ||||
| 		"sum": { | ||||
| 			name: "sum", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpArrayNumber}}, | ||||
| 			}, | ||||
| 			handler: jpfSum, | ||||
| 		}, | ||||
| 		"min": { | ||||
| 			name: "min", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpArrayNumber, jpArrayString}}, | ||||
| 			}, | ||||
| 			handler: jpfMin, | ||||
| 		}, | ||||
| 		"min_by": { | ||||
| 			name: "min_by", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpArray}}, | ||||
| 				{types: []jpType{jpExpref}}, | ||||
| 			}, | ||||
| 			handler:   jpfMinBy, | ||||
| 			hasExpRef: true, | ||||
| 		}, | ||||
| 		"type": { | ||||
| 			name: "type", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpAny}}, | ||||
| 			}, | ||||
| 			handler: jpfType, | ||||
| 		}, | ||||
| 		"keys": { | ||||
| 			name: "keys", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpObject}}, | ||||
| 			}, | ||||
| 			handler: jpfKeys, | ||||
| 		}, | ||||
| 		"values": { | ||||
| 			name: "values", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpObject}}, | ||||
| 			}, | ||||
| 			handler: jpfValues, | ||||
| 		}, | ||||
| 		"sort": { | ||||
| 			name: "sort", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpArrayString, jpArrayNumber}}, | ||||
| 			}, | ||||
| 			handler: jpfSort, | ||||
| 		}, | ||||
| 		"sort_by": { | ||||
| 			name: "sort_by", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpArray}}, | ||||
| 				{types: []jpType{jpExpref}}, | ||||
| 			}, | ||||
| 			handler:   jpfSortBy, | ||||
| 			hasExpRef: true, | ||||
| 		}, | ||||
| 		"join": { | ||||
| 			name: "join", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpString}}, | ||||
| 				{types: []jpType{jpArrayString}}, | ||||
| 			}, | ||||
| 			handler: jpfJoin, | ||||
| 		}, | ||||
| 		"reverse": { | ||||
| 			name: "reverse", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpArray, jpString}}, | ||||
| 			}, | ||||
| 			handler: jpfReverse, | ||||
| 		}, | ||||
| 		"to_array": { | ||||
| 			name: "to_array", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpAny}}, | ||||
| 			}, | ||||
| 			handler: jpfToArray, | ||||
| 		}, | ||||
| 		"to_string": { | ||||
| 			name: "to_string", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpAny}}, | ||||
| 			}, | ||||
| 			handler: jpfToString, | ||||
| 		}, | ||||
| 		"to_number": { | ||||
| 			name: "to_number", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpAny}}, | ||||
| 			}, | ||||
| 			handler: jpfToNumber, | ||||
| 		}, | ||||
| 		"not_null": { | ||||
| 			name: "not_null", | ||||
| 			arguments: []argSpec{ | ||||
| 				{types: []jpType{jpAny}, variadic: true}, | ||||
| 			}, | ||||
| 			handler: jpfNotNull, | ||||
| 		}, | ||||
| 	} | ||||
| 	return caller | ||||
| } | ||||
|  | ||||
| func (e *functionEntry) resolveArgs(arguments []interface{}) ([]interface{}, error) { | ||||
| 	if len(e.arguments) == 0 { | ||||
| 		return arguments, nil | ||||
| 	} | ||||
| 	if !e.arguments[len(e.arguments)-1].variadic { | ||||
| 		if len(e.arguments) != len(arguments) { | ||||
| 			return nil, errors.New("incorrect number of args") | ||||
| 		} | ||||
| 		for i, spec := range e.arguments { | ||||
| 			userArg := arguments[i] | ||||
| 			err := spec.typeCheck(userArg) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 		return arguments, nil | ||||
| 	} | ||||
| 	if len(arguments) < len(e.arguments) { | ||||
| 		return nil, errors.New("Invalid arity.") | ||||
| 	} | ||||
| 	return arguments, nil | ||||
| } | ||||
|  | ||||
| func (a *argSpec) typeCheck(arg interface{}) error { | ||||
| 	for _, t := range a.types { | ||||
| 		switch t { | ||||
| 		case jpNumber: | ||||
| 			if _, ok := arg.(float64); ok { | ||||
| 				return nil | ||||
| 			} | ||||
| 		case jpString: | ||||
| 			if _, ok := arg.(string); ok { | ||||
| 				return nil | ||||
| 			} | ||||
| 		case jpArray: | ||||
| 			if isSliceType(arg) { | ||||
| 				return nil | ||||
| 			} | ||||
| 		case jpObject: | ||||
| 			if _, ok := arg.(map[string]interface{}); ok { | ||||
| 				return nil | ||||
| 			} | ||||
| 		case jpArrayNumber: | ||||
| 			if _, ok := toArrayNum(arg); ok { | ||||
| 				return nil | ||||
| 			} | ||||
| 		case jpArrayString: | ||||
| 			if _, ok := toArrayStr(arg); ok { | ||||
| 				return nil | ||||
| 			} | ||||
| 		case jpAny: | ||||
| 			return nil | ||||
| 		case jpExpref: | ||||
| 			if _, ok := arg.(expRef); ok { | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return fmt.Errorf("Invalid type for: %v, expected: %#v", arg, a.types) | ||||
| } | ||||
|  | ||||
| func (f *functionCaller) CallFunction(name string, arguments []interface{}, intr *treeInterpreter) (interface{}, error) { | ||||
| 	entry, ok := f.functionTable[name] | ||||
| 	if !ok { | ||||
| 		return nil, errors.New("unknown function: " + name) | ||||
| 	} | ||||
| 	resolvedArgs, err := entry.resolveArgs(arguments) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if entry.hasExpRef { | ||||
| 		var extra []interface{} | ||||
| 		extra = append(extra, intr) | ||||
| 		resolvedArgs = append(extra, resolvedArgs...) | ||||
| 	} | ||||
| 	return entry.handler(resolvedArgs) | ||||
| } | ||||
|  | ||||
| func jpfAbs(arguments []interface{}) (interface{}, error) { | ||||
| 	num := arguments[0].(float64) | ||||
| 	return math.Abs(num), nil | ||||
| } | ||||
|  | ||||
| func jpfLength(arguments []interface{}) (interface{}, error) { | ||||
| 	arg := arguments[0] | ||||
| 	if c, ok := arg.(string); ok { | ||||
| 		return float64(utf8.RuneCountInString(c)), nil | ||||
| 	} else if isSliceType(arg) { | ||||
| 		v := reflect.ValueOf(arg) | ||||
| 		return float64(v.Len()), nil | ||||
| 	} else if c, ok := arg.(map[string]interface{}); ok { | ||||
| 		return float64(len(c)), nil | ||||
| 	} | ||||
| 	return nil, errors.New("could not compute length()") | ||||
| } | ||||
|  | ||||
| func jpfStartsWith(arguments []interface{}) (interface{}, error) { | ||||
| 	search := arguments[0].(string) | ||||
| 	prefix := arguments[1].(string) | ||||
| 	return strings.HasPrefix(search, prefix), nil | ||||
| } | ||||
|  | ||||
| func jpfAvg(arguments []interface{}) (interface{}, error) { | ||||
| 	// We've already type checked the value so we can safely use | ||||
| 	// type assertions. | ||||
| 	args := arguments[0].([]interface{}) | ||||
| 	length := float64(len(args)) | ||||
| 	numerator := 0.0 | ||||
| 	for _, n := range args { | ||||
| 		numerator += n.(float64) | ||||
| 	} | ||||
| 	return numerator / length, nil | ||||
| } | ||||
| func jpfCeil(arguments []interface{}) (interface{}, error) { | ||||
| 	val := arguments[0].(float64) | ||||
| 	return math.Ceil(val), nil | ||||
| } | ||||
| func jpfContains(arguments []interface{}) (interface{}, error) { | ||||
| 	search := arguments[0] | ||||
| 	el := arguments[1] | ||||
| 	if searchStr, ok := search.(string); ok { | ||||
| 		if elStr, ok := el.(string); ok { | ||||
| 			return strings.Index(searchStr, elStr) != -1, nil | ||||
| 		} | ||||
| 		return false, nil | ||||
| 	} | ||||
| 	// Otherwise this is a generic contains for []interface{} | ||||
| 	general := search.([]interface{}) | ||||
| 	for _, item := range general { | ||||
| 		if item == el { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return false, nil | ||||
| } | ||||
| func jpfEndsWith(arguments []interface{}) (interface{}, error) { | ||||
| 	search := arguments[0].(string) | ||||
| 	suffix := arguments[1].(string) | ||||
| 	return strings.HasSuffix(search, suffix), nil | ||||
| } | ||||
| func jpfFloor(arguments []interface{}) (interface{}, error) { | ||||
| 	val := arguments[0].(float64) | ||||
| 	return math.Floor(val), nil | ||||
| } | ||||
| func jpfMap(arguments []interface{}) (interface{}, error) { | ||||
| 	intr := arguments[0].(*treeInterpreter) | ||||
| 	exp := arguments[1].(expRef) | ||||
| 	node := exp.ref | ||||
| 	arr := arguments[2].([]interface{}) | ||||
| 	mapped := make([]interface{}, 0, len(arr)) | ||||
| 	for _, value := range arr { | ||||
| 		current, err := intr.Execute(node, value) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		mapped = append(mapped, current) | ||||
| 	} | ||||
| 	return mapped, nil | ||||
| } | ||||
| func jpfMax(arguments []interface{}) (interface{}, error) { | ||||
| 	if items, ok := toArrayNum(arguments[0]); ok { | ||||
| 		if len(items) == 0 { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		if len(items) == 1 { | ||||
| 			return items[0], nil | ||||
| 		} | ||||
| 		best := items[0] | ||||
| 		for _, item := range items[1:] { | ||||
| 			if item > best { | ||||
| 				best = item | ||||
| 			} | ||||
| 		} | ||||
| 		return best, nil | ||||
| 	} | ||||
| 	// Otherwise we're dealing with a max() of strings. | ||||
| 	items, _ := toArrayStr(arguments[0]) | ||||
| 	if len(items) == 0 { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	if len(items) == 1 { | ||||
| 		return items[0], nil | ||||
| 	} | ||||
| 	best := items[0] | ||||
| 	for _, item := range items[1:] { | ||||
| 		if item > best { | ||||
| 			best = item | ||||
| 		} | ||||
| 	} | ||||
| 	return best, nil | ||||
| } | ||||
| func jpfMerge(arguments []interface{}) (interface{}, error) { | ||||
| 	final := make(map[string]interface{}) | ||||
| 	for _, m := range arguments { | ||||
| 		mapped := m.(map[string]interface{}) | ||||
| 		for key, value := range mapped { | ||||
| 			final[key] = value | ||||
| 		} | ||||
| 	} | ||||
| 	return final, nil | ||||
| } | ||||
| func jpfMaxBy(arguments []interface{}) (interface{}, error) { | ||||
| 	intr := arguments[0].(*treeInterpreter) | ||||
| 	arr := arguments[1].([]interface{}) | ||||
| 	exp := arguments[2].(expRef) | ||||
| 	node := exp.ref | ||||
| 	if len(arr) == 0 { | ||||
| 		return nil, nil | ||||
| 	} else if len(arr) == 1 { | ||||
| 		return arr[0], nil | ||||
| 	} | ||||
| 	start, err := intr.Execute(node, arr[0]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	switch t := start.(type) { | ||||
| 	case float64: | ||||
| 		bestVal := t | ||||
| 		bestItem := arr[0] | ||||
| 		for _, item := range arr[1:] { | ||||
| 			result, err := intr.Execute(node, item) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			current, ok := result.(float64) | ||||
| 			if !ok { | ||||
| 				return nil, errors.New("invalid type, must be number") | ||||
| 			} | ||||
| 			if current > bestVal { | ||||
| 				bestVal = current | ||||
| 				bestItem = item | ||||
| 			} | ||||
| 		} | ||||
| 		return bestItem, nil | ||||
| 	case string: | ||||
| 		bestVal := t | ||||
| 		bestItem := arr[0] | ||||
| 		for _, item := range arr[1:] { | ||||
| 			result, err := intr.Execute(node, item) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			current, ok := result.(string) | ||||
| 			if !ok { | ||||
| 				return nil, errors.New("invalid type, must be string") | ||||
| 			} | ||||
| 			if current > bestVal { | ||||
| 				bestVal = current | ||||
| 				bestItem = item | ||||
| 			} | ||||
| 		} | ||||
| 		return bestItem, nil | ||||
| 	default: | ||||
| 		return nil, errors.New("invalid type, must be number of string") | ||||
| 	} | ||||
| } | ||||
| func jpfSum(arguments []interface{}) (interface{}, error) { | ||||
| 	items, _ := toArrayNum(arguments[0]) | ||||
| 	sum := 0.0 | ||||
| 	for _, item := range items { | ||||
| 		sum += item | ||||
| 	} | ||||
| 	return sum, nil | ||||
| } | ||||
|  | ||||
| func jpfMin(arguments []interface{}) (interface{}, error) { | ||||
| 	if items, ok := toArrayNum(arguments[0]); ok { | ||||
| 		if len(items) == 0 { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		if len(items) == 1 { | ||||
| 			return items[0], nil | ||||
| 		} | ||||
| 		best := items[0] | ||||
| 		for _, item := range items[1:] { | ||||
| 			if item < best { | ||||
| 				best = item | ||||
| 			} | ||||
| 		} | ||||
| 		return best, nil | ||||
| 	} | ||||
| 	items, _ := toArrayStr(arguments[0]) | ||||
| 	if len(items) == 0 { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	if len(items) == 1 { | ||||
| 		return items[0], nil | ||||
| 	} | ||||
| 	best := items[0] | ||||
| 	for _, item := range items[1:] { | ||||
| 		if item < best { | ||||
| 			best = item | ||||
| 		} | ||||
| 	} | ||||
| 	return best, nil | ||||
| } | ||||
|  | ||||
| func jpfMinBy(arguments []interface{}) (interface{}, error) { | ||||
| 	intr := arguments[0].(*treeInterpreter) | ||||
| 	arr := arguments[1].([]interface{}) | ||||
| 	exp := arguments[2].(expRef) | ||||
| 	node := exp.ref | ||||
| 	if len(arr) == 0 { | ||||
| 		return nil, nil | ||||
| 	} else if len(arr) == 1 { | ||||
| 		return arr[0], nil | ||||
| 	} | ||||
| 	start, err := intr.Execute(node, arr[0]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if t, ok := start.(float64); ok { | ||||
| 		bestVal := t | ||||
| 		bestItem := arr[0] | ||||
| 		for _, item := range arr[1:] { | ||||
| 			result, err := intr.Execute(node, item) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			current, ok := result.(float64) | ||||
| 			if !ok { | ||||
| 				return nil, errors.New("invalid type, must be number") | ||||
| 			} | ||||
| 			if current < bestVal { | ||||
| 				bestVal = current | ||||
| 				bestItem = item | ||||
| 			} | ||||
| 		} | ||||
| 		return bestItem, nil | ||||
| 	} else if t, ok := start.(string); ok { | ||||
| 		bestVal := t | ||||
| 		bestItem := arr[0] | ||||
| 		for _, item := range arr[1:] { | ||||
| 			result, err := intr.Execute(node, item) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			current, ok := result.(string) | ||||
| 			if !ok { | ||||
| 				return nil, errors.New("invalid type, must be string") | ||||
| 			} | ||||
| 			if current < bestVal { | ||||
| 				bestVal = current | ||||
| 				bestItem = item | ||||
| 			} | ||||
| 		} | ||||
| 		return bestItem, nil | ||||
| 	} else { | ||||
| 		return nil, errors.New("invalid type, must be number of string") | ||||
| 	} | ||||
| } | ||||
| func jpfType(arguments []interface{}) (interface{}, error) { | ||||
| 	arg := arguments[0] | ||||
| 	if _, ok := arg.(float64); ok { | ||||
| 		return "number", nil | ||||
| 	} | ||||
| 	if _, ok := arg.(string); ok { | ||||
| 		return "string", nil | ||||
| 	} | ||||
| 	if _, ok := arg.([]interface{}); ok { | ||||
| 		return "array", nil | ||||
| 	} | ||||
| 	if _, ok := arg.(map[string]interface{}); ok { | ||||
| 		return "object", nil | ||||
| 	} | ||||
| 	if arg == nil { | ||||
| 		return "null", nil | ||||
| 	} | ||||
| 	if arg == true || arg == false { | ||||
| 		return "boolean", nil | ||||
| 	} | ||||
| 	return nil, errors.New("unknown type") | ||||
| } | ||||
| func jpfKeys(arguments []interface{}) (interface{}, error) { | ||||
| 	arg := arguments[0].(map[string]interface{}) | ||||
| 	collected := make([]interface{}, 0, len(arg)) | ||||
| 	for key := range arg { | ||||
| 		collected = append(collected, key) | ||||
| 	} | ||||
| 	return collected, nil | ||||
| } | ||||
| func jpfValues(arguments []interface{}) (interface{}, error) { | ||||
| 	arg := arguments[0].(map[string]interface{}) | ||||
| 	collected := make([]interface{}, 0, len(arg)) | ||||
| 	for _, value := range arg { | ||||
| 		collected = append(collected, value) | ||||
| 	} | ||||
| 	return collected, nil | ||||
| } | ||||
| func jpfSort(arguments []interface{}) (interface{}, error) { | ||||
| 	if items, ok := toArrayNum(arguments[0]); ok { | ||||
| 		d := sort.Float64Slice(items) | ||||
| 		sort.Stable(d) | ||||
| 		final := make([]interface{}, len(d)) | ||||
| 		for i, val := range d { | ||||
| 			final[i] = val | ||||
| 		} | ||||
| 		return final, nil | ||||
| 	} | ||||
| 	// Otherwise we're dealing with sort()'ing strings. | ||||
| 	items, _ := toArrayStr(arguments[0]) | ||||
| 	d := sort.StringSlice(items) | ||||
| 	sort.Stable(d) | ||||
| 	final := make([]interface{}, len(d)) | ||||
| 	for i, val := range d { | ||||
| 		final[i] = val | ||||
| 	} | ||||
| 	return final, nil | ||||
| } | ||||
| func jpfSortBy(arguments []interface{}) (interface{}, error) { | ||||
| 	intr := arguments[0].(*treeInterpreter) | ||||
| 	arr := arguments[1].([]interface{}) | ||||
| 	exp := arguments[2].(expRef) | ||||
| 	node := exp.ref | ||||
| 	if len(arr) == 0 { | ||||
| 		return arr, nil | ||||
| 	} else if len(arr) == 1 { | ||||
| 		return arr, nil | ||||
| 	} | ||||
| 	start, err := intr.Execute(node, arr[0]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if _, ok := start.(float64); ok { | ||||
| 		sortable := &byExprFloat{intr, node, arr, false} | ||||
| 		sort.Stable(sortable) | ||||
| 		if sortable.hasError { | ||||
| 			return nil, errors.New("error in sort_by comparison") | ||||
| 		} | ||||
| 		return arr, nil | ||||
| 	} else if _, ok := start.(string); ok { | ||||
| 		sortable := &byExprString{intr, node, arr, false} | ||||
| 		sort.Stable(sortable) | ||||
| 		if sortable.hasError { | ||||
| 			return nil, errors.New("error in sort_by comparison") | ||||
| 		} | ||||
| 		return arr, nil | ||||
| 	} else { | ||||
| 		return nil, errors.New("invalid type, must be number of string") | ||||
| 	} | ||||
| } | ||||
| func jpfJoin(arguments []interface{}) (interface{}, error) { | ||||
| 	sep := arguments[0].(string) | ||||
| 	// We can't just do arguments[1].([]string), we have to | ||||
| 	// manually convert each item to a string. | ||||
| 	arrayStr := []string{} | ||||
| 	for _, item := range arguments[1].([]interface{}) { | ||||
| 		arrayStr = append(arrayStr, item.(string)) | ||||
| 	} | ||||
| 	return strings.Join(arrayStr, sep), nil | ||||
| } | ||||
| func jpfReverse(arguments []interface{}) (interface{}, error) { | ||||
| 	if s, ok := arguments[0].(string); ok { | ||||
| 		r := []rune(s) | ||||
| 		for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { | ||||
| 			r[i], r[j] = r[j], r[i] | ||||
| 		} | ||||
| 		return string(r), nil | ||||
| 	} | ||||
| 	items := arguments[0].([]interface{}) | ||||
| 	length := len(items) | ||||
| 	reversed := make([]interface{}, length) | ||||
| 	for i, item := range items { | ||||
| 		reversed[length-(i+1)] = item | ||||
| 	} | ||||
| 	return reversed, nil | ||||
| } | ||||
| func jpfToArray(arguments []interface{}) (interface{}, error) { | ||||
| 	if _, ok := arguments[0].([]interface{}); ok { | ||||
| 		return arguments[0], nil | ||||
| 	} | ||||
| 	return arguments[:1:1], nil | ||||
| } | ||||
| func jpfToString(arguments []interface{}) (interface{}, error) { | ||||
| 	if v, ok := arguments[0].(string); ok { | ||||
| 		return v, nil | ||||
| 	} | ||||
| 	result, err := json.Marshal(arguments[0]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return string(result), nil | ||||
| } | ||||
| func jpfToNumber(arguments []interface{}) (interface{}, error) { | ||||
| 	arg := arguments[0] | ||||
| 	if v, ok := arg.(float64); ok { | ||||
| 		return v, nil | ||||
| 	} | ||||
| 	if v, ok := arg.(string); ok { | ||||
| 		conv, err := strconv.ParseFloat(v, 64) | ||||
| 		if err != nil { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		return conv, nil | ||||
| 	} | ||||
| 	if _, ok := arg.([]interface{}); ok { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	if _, ok := arg.(map[string]interface{}); ok { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	if arg == nil { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	if arg == true || arg == false { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	return nil, errors.New("unknown type") | ||||
| } | ||||
| func jpfNotNull(arguments []interface{}) (interface{}, error) { | ||||
| 	for _, arg := range arguments { | ||||
| 		if arg != nil { | ||||
| 			return arg, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, nil | ||||
| } | ||||
							
								
								
									
										418
									
								
								vendor/github.com/jmespath/go-jmespath/interpreter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										418
									
								
								vendor/github.com/jmespath/go-jmespath/interpreter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,418 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"reflect" | ||||
| 	"unicode" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| /* This is a tree based interpreter.  It walks the AST and directly | ||||
|    interprets the AST to search through a JSON document. | ||||
| */ | ||||
|  | ||||
| type treeInterpreter struct { | ||||
| 	fCall *functionCaller | ||||
| } | ||||
|  | ||||
| func newInterpreter() *treeInterpreter { | ||||
| 	interpreter := treeInterpreter{} | ||||
| 	interpreter.fCall = newFunctionCaller() | ||||
| 	return &interpreter | ||||
| } | ||||
|  | ||||
| type expRef struct { | ||||
| 	ref ASTNode | ||||
| } | ||||
|  | ||||
| // Execute takes an ASTNode and input data and interprets the AST directly. | ||||
| // It will produce the result of applying the JMESPath expression associated | ||||
| // with the ASTNode to the input data "value". | ||||
| func (intr *treeInterpreter) Execute(node ASTNode, value interface{}) (interface{}, error) { | ||||
| 	switch node.nodeType { | ||||
| 	case ASTComparator: | ||||
| 		left, err := intr.Execute(node.children[0], value) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		right, err := intr.Execute(node.children[1], value) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		switch node.value { | ||||
| 		case tEQ: | ||||
| 			return objsEqual(left, right), nil | ||||
| 		case tNE: | ||||
| 			return !objsEqual(left, right), nil | ||||
| 		} | ||||
| 		leftNum, ok := left.(float64) | ||||
| 		if !ok { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		rightNum, ok := right.(float64) | ||||
| 		if !ok { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		switch node.value { | ||||
| 		case tGT: | ||||
| 			return leftNum > rightNum, nil | ||||
| 		case tGTE: | ||||
| 			return leftNum >= rightNum, nil | ||||
| 		case tLT: | ||||
| 			return leftNum < rightNum, nil | ||||
| 		case tLTE: | ||||
| 			return leftNum <= rightNum, nil | ||||
| 		} | ||||
| 	case ASTExpRef: | ||||
| 		return expRef{ref: node.children[0]}, nil | ||||
| 	case ASTFunctionExpression: | ||||
| 		resolvedArgs := []interface{}{} | ||||
| 		for _, arg := range node.children { | ||||
| 			current, err := intr.Execute(arg, value) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			resolvedArgs = append(resolvedArgs, current) | ||||
| 		} | ||||
| 		return intr.fCall.CallFunction(node.value.(string), resolvedArgs, intr) | ||||
| 	case ASTField: | ||||
| 		if m, ok := value.(map[string]interface{}); ok { | ||||
| 			key := node.value.(string) | ||||
| 			return m[key], nil | ||||
| 		} | ||||
| 		return intr.fieldFromStruct(node.value.(string), value) | ||||
| 	case ASTFilterProjection: | ||||
| 		left, err := intr.Execute(node.children[0], value) | ||||
| 		if err != nil { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		sliceType, ok := left.([]interface{}) | ||||
| 		if !ok { | ||||
| 			if isSliceType(left) { | ||||
| 				return intr.filterProjectionWithReflection(node, left) | ||||
| 			} | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		compareNode := node.children[2] | ||||
| 		collected := []interface{}{} | ||||
| 		for _, element := range sliceType { | ||||
| 			result, err := intr.Execute(compareNode, element) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			if !isFalse(result) { | ||||
| 				current, err := intr.Execute(node.children[1], element) | ||||
| 				if err != nil { | ||||
| 					return nil, err | ||||
| 				} | ||||
| 				if current != nil { | ||||
| 					collected = append(collected, current) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return collected, nil | ||||
| 	case ASTFlatten: | ||||
| 		left, err := intr.Execute(node.children[0], value) | ||||
| 		if err != nil { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		sliceType, ok := left.([]interface{}) | ||||
| 		if !ok { | ||||
| 			// If we can't type convert to []interface{}, there's | ||||
| 			// a chance this could still work via reflection if we're | ||||
| 			// dealing with user provided types. | ||||
| 			if isSliceType(left) { | ||||
| 				return intr.flattenWithReflection(left) | ||||
| 			} | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		flattened := []interface{}{} | ||||
| 		for _, element := range sliceType { | ||||
| 			if elementSlice, ok := element.([]interface{}); ok { | ||||
| 				flattened = append(flattened, elementSlice...) | ||||
| 			} else if isSliceType(element) { | ||||
| 				reflectFlat := []interface{}{} | ||||
| 				v := reflect.ValueOf(element) | ||||
| 				for i := 0; i < v.Len(); i++ { | ||||
| 					reflectFlat = append(reflectFlat, v.Index(i).Interface()) | ||||
| 				} | ||||
| 				flattened = append(flattened, reflectFlat...) | ||||
| 			} else { | ||||
| 				flattened = append(flattened, element) | ||||
| 			} | ||||
| 		} | ||||
| 		return flattened, nil | ||||
| 	case ASTIdentity, ASTCurrentNode: | ||||
| 		return value, nil | ||||
| 	case ASTIndex: | ||||
| 		if sliceType, ok := value.([]interface{}); ok { | ||||
| 			index := node.value.(int) | ||||
| 			if index < 0 { | ||||
| 				index += len(sliceType) | ||||
| 			} | ||||
| 			if index < len(sliceType) && index >= 0 { | ||||
| 				return sliceType[index], nil | ||||
| 			} | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		// Otherwise try via reflection. | ||||
| 		rv := reflect.ValueOf(value) | ||||
| 		if rv.Kind() == reflect.Slice { | ||||
| 			index := node.value.(int) | ||||
| 			if index < 0 { | ||||
| 				index += rv.Len() | ||||
| 			} | ||||
| 			if index < rv.Len() && index >= 0 { | ||||
| 				v := rv.Index(index) | ||||
| 				return v.Interface(), nil | ||||
| 			} | ||||
| 		} | ||||
| 		return nil, nil | ||||
| 	case ASTKeyValPair: | ||||
| 		return intr.Execute(node.children[0], value) | ||||
| 	case ASTLiteral: | ||||
| 		return node.value, nil | ||||
| 	case ASTMultiSelectHash: | ||||
| 		if value == nil { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		collected := make(map[string]interface{}) | ||||
| 		for _, child := range node.children { | ||||
| 			current, err := intr.Execute(child, value) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			key := child.value.(string) | ||||
| 			collected[key] = current | ||||
| 		} | ||||
| 		return collected, nil | ||||
| 	case ASTMultiSelectList: | ||||
| 		if value == nil { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		collected := []interface{}{} | ||||
| 		for _, child := range node.children { | ||||
| 			current, err := intr.Execute(child, value) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			collected = append(collected, current) | ||||
| 		} | ||||
| 		return collected, nil | ||||
| 	case ASTOrExpression: | ||||
| 		matched, err := intr.Execute(node.children[0], value) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if isFalse(matched) { | ||||
| 			matched, err = intr.Execute(node.children[1], value) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 		return matched, nil | ||||
| 	case ASTAndExpression: | ||||
| 		matched, err := intr.Execute(node.children[0], value) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if isFalse(matched) { | ||||
| 			return matched, nil | ||||
| 		} | ||||
| 		return intr.Execute(node.children[1], value) | ||||
| 	case ASTNotExpression: | ||||
| 		matched, err := intr.Execute(node.children[0], value) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if isFalse(matched) { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		return false, nil | ||||
| 	case ASTPipe: | ||||
| 		result := value | ||||
| 		var err error | ||||
| 		for _, child := range node.children { | ||||
| 			result, err = intr.Execute(child, result) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 		return result, nil | ||||
| 	case ASTProjection: | ||||
| 		left, err := intr.Execute(node.children[0], value) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		sliceType, ok := left.([]interface{}) | ||||
| 		if !ok { | ||||
| 			if isSliceType(left) { | ||||
| 				return intr.projectWithReflection(node, left) | ||||
| 			} | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		collected := []interface{}{} | ||||
| 		var current interface{} | ||||
| 		for _, element := range sliceType { | ||||
| 			current, err = intr.Execute(node.children[1], element) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			if current != nil { | ||||
| 				collected = append(collected, current) | ||||
| 			} | ||||
| 		} | ||||
| 		return collected, nil | ||||
| 	case ASTSubexpression, ASTIndexExpression: | ||||
| 		left, err := intr.Execute(node.children[0], value) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return intr.Execute(node.children[1], left) | ||||
| 	case ASTSlice: | ||||
| 		sliceType, ok := value.([]interface{}) | ||||
| 		if !ok { | ||||
| 			if isSliceType(value) { | ||||
| 				return intr.sliceWithReflection(node, value) | ||||
| 			} | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		parts := node.value.([]*int) | ||||
| 		sliceParams := make([]sliceParam, 3) | ||||
| 		for i, part := range parts { | ||||
| 			if part != nil { | ||||
| 				sliceParams[i].Specified = true | ||||
| 				sliceParams[i].N = *part | ||||
| 			} | ||||
| 		} | ||||
| 		return slice(sliceType, sliceParams) | ||||
| 	case ASTValueProjection: | ||||
| 		left, err := intr.Execute(node.children[0], value) | ||||
| 		if err != nil { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		mapType, ok := left.(map[string]interface{}) | ||||
| 		if !ok { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		values := make([]interface{}, len(mapType)) | ||||
| 		for _, value := range mapType { | ||||
| 			values = append(values, value) | ||||
| 		} | ||||
| 		collected := []interface{}{} | ||||
| 		for _, element := range values { | ||||
| 			current, err := intr.Execute(node.children[1], element) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			if current != nil { | ||||
| 				collected = append(collected, current) | ||||
| 			} | ||||
| 		} | ||||
| 		return collected, nil | ||||
| 	} | ||||
| 	return nil, errors.New("Unknown AST node: " + node.nodeType.String()) | ||||
| } | ||||
|  | ||||
| func (intr *treeInterpreter) fieldFromStruct(key string, value interface{}) (interface{}, error) { | ||||
| 	rv := reflect.ValueOf(value) | ||||
| 	first, n := utf8.DecodeRuneInString(key) | ||||
| 	fieldName := string(unicode.ToUpper(first)) + key[n:] | ||||
| 	if rv.Kind() == reflect.Struct { | ||||
| 		v := rv.FieldByName(fieldName) | ||||
| 		if !v.IsValid() { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		return v.Interface(), nil | ||||
| 	} else if rv.Kind() == reflect.Ptr { | ||||
| 		// Handle multiple levels of indirection? | ||||
| 		if rv.IsNil() { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		rv = rv.Elem() | ||||
| 		v := rv.FieldByName(fieldName) | ||||
| 		if !v.IsValid() { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		return v.Interface(), nil | ||||
| 	} | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| func (intr *treeInterpreter) flattenWithReflection(value interface{}) (interface{}, error) { | ||||
| 	v := reflect.ValueOf(value) | ||||
| 	flattened := []interface{}{} | ||||
| 	for i := 0; i < v.Len(); i++ { | ||||
| 		element := v.Index(i).Interface() | ||||
| 		if reflect.TypeOf(element).Kind() == reflect.Slice { | ||||
| 			// Then insert the contents of the element | ||||
| 			// slice into the flattened slice, | ||||
| 			// i.e flattened = append(flattened, mySlice...) | ||||
| 			elementV := reflect.ValueOf(element) | ||||
| 			for j := 0; j < elementV.Len(); j++ { | ||||
| 				flattened = append( | ||||
| 					flattened, elementV.Index(j).Interface()) | ||||
| 			} | ||||
| 		} else { | ||||
| 			flattened = append(flattened, element) | ||||
| 		} | ||||
| 	} | ||||
| 	return flattened, nil | ||||
| } | ||||
|  | ||||
| func (intr *treeInterpreter) sliceWithReflection(node ASTNode, value interface{}) (interface{}, error) { | ||||
| 	v := reflect.ValueOf(value) | ||||
| 	parts := node.value.([]*int) | ||||
| 	sliceParams := make([]sliceParam, 3) | ||||
| 	for i, part := range parts { | ||||
| 		if part != nil { | ||||
| 			sliceParams[i].Specified = true | ||||
| 			sliceParams[i].N = *part | ||||
| 		} | ||||
| 	} | ||||
| 	final := []interface{}{} | ||||
| 	for i := 0; i < v.Len(); i++ { | ||||
| 		element := v.Index(i).Interface() | ||||
| 		final = append(final, element) | ||||
| 	} | ||||
| 	return slice(final, sliceParams) | ||||
| } | ||||
|  | ||||
| func (intr *treeInterpreter) filterProjectionWithReflection(node ASTNode, value interface{}) (interface{}, error) { | ||||
| 	compareNode := node.children[2] | ||||
| 	collected := []interface{}{} | ||||
| 	v := reflect.ValueOf(value) | ||||
| 	for i := 0; i < v.Len(); i++ { | ||||
| 		element := v.Index(i).Interface() | ||||
| 		result, err := intr.Execute(compareNode, element) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if !isFalse(result) { | ||||
| 			current, err := intr.Execute(node.children[1], element) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			if current != nil { | ||||
| 				collected = append(collected, current) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return collected, nil | ||||
| } | ||||
|  | ||||
| func (intr *treeInterpreter) projectWithReflection(node ASTNode, value interface{}) (interface{}, error) { | ||||
| 	collected := []interface{}{} | ||||
| 	v := reflect.ValueOf(value) | ||||
| 	for i := 0; i < v.Len(); i++ { | ||||
| 		element := v.Index(i).Interface() | ||||
| 		result, err := intr.Execute(node.children[1], element) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if result != nil { | ||||
| 			collected = append(collected, result) | ||||
| 		} | ||||
| 	} | ||||
| 	return collected, nil | ||||
| } | ||||
							
								
								
									
										221
									
								
								vendor/github.com/jmespath/go-jmespath/interpreter_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								vendor/github.com/jmespath/go-jmespath/interpreter_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,221 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| type scalars struct { | ||||
| 	Foo string | ||||
| 	Bar string | ||||
| } | ||||
|  | ||||
| type sliceType struct { | ||||
| 	A string | ||||
| 	B []scalars | ||||
| 	C []*scalars | ||||
| } | ||||
|  | ||||
| type benchmarkStruct struct { | ||||
| 	Fooasdfasdfasdfasdf string | ||||
| } | ||||
|  | ||||
| type benchmarkNested struct { | ||||
| 	Fooasdfasdfasdfasdf nestedA | ||||
| } | ||||
|  | ||||
| type nestedA struct { | ||||
| 	Fooasdfasdfasdfasdf nestedB | ||||
| } | ||||
|  | ||||
| type nestedB struct { | ||||
| 	Fooasdfasdfasdfasdf nestedC | ||||
| } | ||||
|  | ||||
| type nestedC struct { | ||||
| 	Fooasdfasdfasdfasdf string | ||||
| } | ||||
|  | ||||
| type nestedSlice struct { | ||||
| 	A []sliceType | ||||
| } | ||||
|  | ||||
| func TestCanSupportEmptyInterface(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := make(map[string]interface{}) | ||||
| 	data["foo"] = "bar" | ||||
| 	result, err := Search("foo", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal("bar", result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportUserDefinedStructsValue(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	s := scalars{Foo: "one", Bar: "bar"} | ||||
| 	result, err := Search("Foo", s) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal("one", result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportUserDefinedStructsRef(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	s := scalars{Foo: "one", Bar: "bar"} | ||||
| 	result, err := Search("Foo", &s) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal("one", result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportStructWithSliceAll(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := sliceType{A: "foo", B: []scalars{{"f1", "b1"}, {"correct", "b2"}}} | ||||
| 	result, err := Search("B[].Foo", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal([]interface{}{"f1", "correct"}, result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportStructWithSlicingExpression(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := sliceType{A: "foo", B: []scalars{{"f1", "b1"}, {"correct", "b2"}}} | ||||
| 	result, err := Search("B[:].Foo", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal([]interface{}{"f1", "correct"}, result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportStructWithFilterProjection(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := sliceType{A: "foo", B: []scalars{{"f1", "b1"}, {"correct", "b2"}}} | ||||
| 	result, err := Search("B[? `true` ].Foo", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal([]interface{}{"f1", "correct"}, result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportStructWithSlice(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := sliceType{A: "foo", B: []scalars{{"f1", "b1"}, {"correct", "b2"}}} | ||||
| 	result, err := Search("B[-1].Foo", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal("correct", result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportStructWithOrExpressions(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := sliceType{A: "foo", C: nil} | ||||
| 	result, err := Search("C || A", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal("foo", result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportStructWithSlicePointer(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := sliceType{A: "foo", C: []*scalars{{"f1", "b1"}, {"correct", "b2"}}} | ||||
| 	result, err := Search("C[-1].Foo", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal("correct", result) | ||||
| } | ||||
|  | ||||
| func TestWillAutomaticallyCapitalizeFieldNames(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	s := scalars{Foo: "one", Bar: "bar"} | ||||
| 	// Note that there's a lower cased "foo" instead of "Foo", | ||||
| 	// but it should still correspond to the Foo field in the | ||||
| 	// scalars struct | ||||
| 	result, err := Search("foo", &s) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal("one", result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportStructWithSliceLowerCased(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := sliceType{A: "foo", B: []scalars{{"f1", "b1"}, {"correct", "b2"}}} | ||||
| 	result, err := Search("b[-1].foo", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal("correct", result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportStructWithNestedPointers(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := struct{ A *struct{ B int } }{} | ||||
| 	result, err := Search("A.B", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Nil(result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportFlattenNestedSlice(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := nestedSlice{A: []sliceType{ | ||||
| 		{B: []scalars{{Foo: "f1a"}, {Foo: "f1b"}}}, | ||||
| 		{B: []scalars{{Foo: "f2a"}, {Foo: "f2b"}}}, | ||||
| 	}} | ||||
| 	result, err := Search("A[].B[].Foo", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal([]interface{}{"f1a", "f1b", "f2a", "f2b"}, result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportFlattenNestedEmptySlice(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := nestedSlice{A: []sliceType{ | ||||
| 		{}, {B: []scalars{{Foo: "a"}}}, | ||||
| 	}} | ||||
| 	result, err := Search("A[].B[].Foo", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal([]interface{}{"a"}, result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportProjectionsWithStructs(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := nestedSlice{A: []sliceType{ | ||||
| 		{A: "first"}, {A: "second"}, {A: "third"}, | ||||
| 	}} | ||||
| 	result, err := Search("A[*].A", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal([]interface{}{"first", "second", "third"}, result) | ||||
| } | ||||
|  | ||||
| func TestCanSupportSliceOfStructsWithFunctions(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	data := []scalars{scalars{"a1", "b1"}, scalars{"a2", "b2"}} | ||||
| 	result, err := Search("length(@)", data) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal(result.(float64), 2.0) | ||||
| } | ||||
|  | ||||
| func BenchmarkInterpretSingleFieldStruct(b *testing.B) { | ||||
| 	intr := newInterpreter() | ||||
| 	parser := NewParser() | ||||
| 	ast, _ := parser.Parse("fooasdfasdfasdfasdf") | ||||
| 	data := benchmarkStruct{"foobarbazqux"} | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		intr.Execute(ast, &data) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkInterpretNestedStruct(b *testing.B) { | ||||
| 	intr := newInterpreter() | ||||
| 	parser := NewParser() | ||||
| 	ast, _ := parser.Parse("fooasdfasdfasdfasdf.fooasdfasdfasdfasdf.fooasdfasdfasdfasdf.fooasdfasdfasdfasdf") | ||||
| 	data := benchmarkNested{ | ||||
| 		nestedA{ | ||||
| 			nestedB{ | ||||
| 				nestedC{"foobarbazqux"}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		intr.Execute(ast, &data) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkInterpretNestedMaps(b *testing.B) { | ||||
| 	jsonData := []byte(`{"fooasdfasdfasdfasdf": {"fooasdfasdfasdfasdf": {"fooasdfasdfasdfasdf": {"fooasdfasdfasdfasdf": "foobarbazqux"}}}}`) | ||||
| 	var data interface{} | ||||
| 	json.Unmarshal(jsonData, &data) | ||||
|  | ||||
| 	intr := newInterpreter() | ||||
| 	parser := NewParser() | ||||
| 	ast, _ := parser.Parse("fooasdfasdfasdfasdf.fooasdfasdfasdfasdf.fooasdfasdfasdfasdf.fooasdfasdfasdfasdf") | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		intr.Execute(ast, data) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										420
									
								
								vendor/github.com/jmespath/go-jmespath/lexer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										420
									
								
								vendor/github.com/jmespath/go-jmespath/lexer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,420 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| type token struct { | ||||
| 	tokenType tokType | ||||
| 	value     string | ||||
| 	position  int | ||||
| 	length    int | ||||
| } | ||||
|  | ||||
| type tokType int | ||||
|  | ||||
| const eof = -1 | ||||
|  | ||||
| // Lexer contains information about the expression being tokenized. | ||||
| type Lexer struct { | ||||
| 	expression string       // The expression provided by the user. | ||||
| 	currentPos int          // The current position in the string. | ||||
| 	lastWidth  int          // The width of the current rune.  This | ||||
| 	buf        bytes.Buffer // Internal buffer used for building up values. | ||||
| } | ||||
|  | ||||
| // SyntaxError is the main error used whenever a lexing or parsing error occurs. | ||||
| type SyntaxError struct { | ||||
| 	msg        string // Error message displayed to user | ||||
| 	Expression string // Expression that generated a SyntaxError | ||||
| 	Offset     int    // The location in the string where the error occurred | ||||
| } | ||||
|  | ||||
| func (e SyntaxError) Error() string { | ||||
| 	// In the future, it would be good to underline the specific | ||||
| 	// location where the error occurred. | ||||
| 	return "SyntaxError: " + e.msg | ||||
| } | ||||
|  | ||||
| // HighlightLocation will show where the syntax error occurred. | ||||
| // It will place a "^" character on a line below the expression | ||||
| // at the point where the syntax error occurred. | ||||
| func (e SyntaxError) HighlightLocation() string { | ||||
| 	return e.Expression + "\n" + strings.Repeat(" ", e.Offset) + "^" | ||||
| } | ||||
|  | ||||
| //go:generate stringer -type=tokType | ||||
| const ( | ||||
| 	tUnknown tokType = iota | ||||
| 	tStar | ||||
| 	tDot | ||||
| 	tFilter | ||||
| 	tFlatten | ||||
| 	tLparen | ||||
| 	tRparen | ||||
| 	tLbracket | ||||
| 	tRbracket | ||||
| 	tLbrace | ||||
| 	tRbrace | ||||
| 	tOr | ||||
| 	tPipe | ||||
| 	tNumber | ||||
| 	tUnquotedIdentifier | ||||
| 	tQuotedIdentifier | ||||
| 	tComma | ||||
| 	tColon | ||||
| 	tLT | ||||
| 	tLTE | ||||
| 	tGT | ||||
| 	tGTE | ||||
| 	tEQ | ||||
| 	tNE | ||||
| 	tJSONLiteral | ||||
| 	tStringLiteral | ||||
| 	tCurrent | ||||
| 	tExpref | ||||
| 	tAnd | ||||
| 	tNot | ||||
| 	tEOF | ||||
| ) | ||||
|  | ||||
| var basicTokens = map[rune]tokType{ | ||||
| 	'.': tDot, | ||||
| 	'*': tStar, | ||||
| 	',': tComma, | ||||
| 	':': tColon, | ||||
| 	'{': tLbrace, | ||||
| 	'}': tRbrace, | ||||
| 	']': tRbracket, // tLbracket not included because it could be "[]" | ||||
| 	'(': tLparen, | ||||
| 	')': tRparen, | ||||
| 	'@': tCurrent, | ||||
| } | ||||
|  | ||||
| // Bit mask for [a-zA-Z_] shifted down 64 bits to fit in a single uint64. | ||||
| // When using this bitmask just be sure to shift the rune down 64 bits | ||||
| // before checking against identifierStartBits. | ||||
| const identifierStartBits uint64 = 576460745995190270 | ||||
|  | ||||
| // Bit mask for [a-zA-Z0-9], 128 bits -> 2 uint64s. | ||||
| var identifierTrailingBits = [2]uint64{287948901175001088, 576460745995190270} | ||||
|  | ||||
| var whiteSpace = map[rune]bool{ | ||||
| 	' ': true, '\t': true, '\n': true, '\r': true, | ||||
| } | ||||
|  | ||||
| func (t token) String() string { | ||||
| 	return fmt.Sprintf("Token{%+v, %s, %d, %d}", | ||||
| 		t.tokenType, t.value, t.position, t.length) | ||||
| } | ||||
|  | ||||
| // NewLexer creates a new JMESPath lexer. | ||||
| func NewLexer() *Lexer { | ||||
| 	lexer := Lexer{} | ||||
| 	return &lexer | ||||
| } | ||||
|  | ||||
| func (lexer *Lexer) next() rune { | ||||
| 	if lexer.currentPos >= len(lexer.expression) { | ||||
| 		lexer.lastWidth = 0 | ||||
| 		return eof | ||||
| 	} | ||||
| 	r, w := utf8.DecodeRuneInString(lexer.expression[lexer.currentPos:]) | ||||
| 	lexer.lastWidth = w | ||||
| 	lexer.currentPos += w | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| func (lexer *Lexer) back() { | ||||
| 	lexer.currentPos -= lexer.lastWidth | ||||
| } | ||||
|  | ||||
| func (lexer *Lexer) peek() rune { | ||||
| 	t := lexer.next() | ||||
| 	lexer.back() | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| // tokenize takes an expression and returns corresponding tokens. | ||||
| func (lexer *Lexer) tokenize(expression string) ([]token, error) { | ||||
| 	var tokens []token | ||||
| 	lexer.expression = expression | ||||
| 	lexer.currentPos = 0 | ||||
| 	lexer.lastWidth = 0 | ||||
| loop: | ||||
| 	for { | ||||
| 		r := lexer.next() | ||||
| 		if identifierStartBits&(1<<(uint64(r)-64)) > 0 { | ||||
| 			t := lexer.consumeUnquotedIdentifier() | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if val, ok := basicTokens[r]; ok { | ||||
| 			// Basic single char token. | ||||
| 			t := token{ | ||||
| 				tokenType: val, | ||||
| 				value:     string(r), | ||||
| 				position:  lexer.currentPos - lexer.lastWidth, | ||||
| 				length:    1, | ||||
| 			} | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '-' || (r >= '0' && r <= '9') { | ||||
| 			t := lexer.consumeNumber() | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '[' { | ||||
| 			t := lexer.consumeLBracket() | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '"' { | ||||
| 			t, err := lexer.consumeQuotedIdentifier() | ||||
| 			if err != nil { | ||||
| 				return tokens, err | ||||
| 			} | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '\'' { | ||||
| 			t, err := lexer.consumeRawStringLiteral() | ||||
| 			if err != nil { | ||||
| 				return tokens, err | ||||
| 			} | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '`' { | ||||
| 			t, err := lexer.consumeLiteral() | ||||
| 			if err != nil { | ||||
| 				return tokens, err | ||||
| 			} | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '|' { | ||||
| 			t := lexer.matchOrElse(r, '|', tOr, tPipe) | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '<' { | ||||
| 			t := lexer.matchOrElse(r, '=', tLTE, tLT) | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '>' { | ||||
| 			t := lexer.matchOrElse(r, '=', tGTE, tGT) | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '!' { | ||||
| 			t := lexer.matchOrElse(r, '=', tNE, tNot) | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '=' { | ||||
| 			t := lexer.matchOrElse(r, '=', tEQ, tUnknown) | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == '&' { | ||||
| 			t := lexer.matchOrElse(r, '&', tAnd, tExpref) | ||||
| 			tokens = append(tokens, t) | ||||
| 		} else if r == eof { | ||||
| 			break loop | ||||
| 		} else if _, ok := whiteSpace[r]; ok { | ||||
| 			// Ignore whitespace | ||||
| 		} else { | ||||
| 			return tokens, lexer.syntaxError(fmt.Sprintf("Unknown char: %s", strconv.QuoteRuneToASCII(r))) | ||||
| 		} | ||||
| 	} | ||||
| 	tokens = append(tokens, token{tEOF, "", len(lexer.expression), 0}) | ||||
| 	return tokens, nil | ||||
| } | ||||
|  | ||||
| // Consume characters until the ending rune "r" is reached. | ||||
| // If the end of the expression is reached before seeing the | ||||
| // terminating rune "r", then an error is returned. | ||||
| // If no error occurs then the matching substring is returned. | ||||
| // The returned string will not include the ending rune. | ||||
| func (lexer *Lexer) consumeUntil(end rune) (string, error) { | ||||
| 	start := lexer.currentPos | ||||
| 	current := lexer.next() | ||||
| 	for current != end && current != eof { | ||||
| 		if current == '\\' && lexer.peek() != eof { | ||||
| 			lexer.next() | ||||
| 		} | ||||
| 		current = lexer.next() | ||||
| 	} | ||||
| 	if lexer.lastWidth == 0 { | ||||
| 		// Then we hit an EOF so we never reached the closing | ||||
| 		// delimiter. | ||||
| 		return "", SyntaxError{ | ||||
| 			msg:        "Unclosed delimiter: " + string(end), | ||||
| 			Expression: lexer.expression, | ||||
| 			Offset:     len(lexer.expression), | ||||
| 		} | ||||
| 	} | ||||
| 	return lexer.expression[start : lexer.currentPos-lexer.lastWidth], nil | ||||
| } | ||||
|  | ||||
| func (lexer *Lexer) consumeLiteral() (token, error) { | ||||
| 	start := lexer.currentPos | ||||
| 	value, err := lexer.consumeUntil('`') | ||||
| 	if err != nil { | ||||
| 		return token{}, err | ||||
| 	} | ||||
| 	value = strings.Replace(value, "\\`", "`", -1) | ||||
| 	return token{ | ||||
| 		tokenType: tJSONLiteral, | ||||
| 		value:     value, | ||||
| 		position:  start, | ||||
| 		length:    len(value), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (lexer *Lexer) consumeRawStringLiteral() (token, error) { | ||||
| 	start := lexer.currentPos | ||||
| 	currentIndex := start | ||||
| 	current := lexer.next() | ||||
| 	for current != '\'' && lexer.peek() != eof { | ||||
| 		if current == '\\' && lexer.peek() == '\'' { | ||||
| 			chunk := lexer.expression[currentIndex : lexer.currentPos-1] | ||||
| 			lexer.buf.WriteString(chunk) | ||||
| 			lexer.buf.WriteString("'") | ||||
| 			lexer.next() | ||||
| 			currentIndex = lexer.currentPos | ||||
| 		} | ||||
| 		current = lexer.next() | ||||
| 	} | ||||
| 	if lexer.lastWidth == 0 { | ||||
| 		// Then we hit an EOF so we never reached the closing | ||||
| 		// delimiter. | ||||
| 		return token{}, SyntaxError{ | ||||
| 			msg:        "Unclosed delimiter: '", | ||||
| 			Expression: lexer.expression, | ||||
| 			Offset:     len(lexer.expression), | ||||
| 		} | ||||
| 	} | ||||
| 	if currentIndex < lexer.currentPos { | ||||
| 		lexer.buf.WriteString(lexer.expression[currentIndex : lexer.currentPos-1]) | ||||
| 	} | ||||
| 	value := lexer.buf.String() | ||||
| 	// Reset the buffer so it can reused again. | ||||
| 	lexer.buf.Reset() | ||||
| 	return token{ | ||||
| 		tokenType: tStringLiteral, | ||||
| 		value:     value, | ||||
| 		position:  start, | ||||
| 		length:    len(value), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (lexer *Lexer) syntaxError(msg string) SyntaxError { | ||||
| 	return SyntaxError{ | ||||
| 		msg:        msg, | ||||
| 		Expression: lexer.expression, | ||||
| 		Offset:     lexer.currentPos - 1, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Checks for a two char token, otherwise matches a single character | ||||
| // token. This is used whenever a two char token overlaps a single | ||||
| // char token, e.g. "||" -> tPipe, "|" -> tOr. | ||||
| func (lexer *Lexer) matchOrElse(first rune, second rune, matchedType tokType, singleCharType tokType) token { | ||||
| 	start := lexer.currentPos - lexer.lastWidth | ||||
| 	nextRune := lexer.next() | ||||
| 	var t token | ||||
| 	if nextRune == second { | ||||
| 		t = token{ | ||||
| 			tokenType: matchedType, | ||||
| 			value:     string(first) + string(second), | ||||
| 			position:  start, | ||||
| 			length:    2, | ||||
| 		} | ||||
| 	} else { | ||||
| 		lexer.back() | ||||
| 		t = token{ | ||||
| 			tokenType: singleCharType, | ||||
| 			value:     string(first), | ||||
| 			position:  start, | ||||
| 			length:    1, | ||||
| 		} | ||||
| 	} | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| func (lexer *Lexer) consumeLBracket() token { | ||||
| 	// There's three options here: | ||||
| 	// 1. A filter expression "[?" | ||||
| 	// 2. A flatten operator "[]" | ||||
| 	// 3. A bare rbracket "[" | ||||
| 	start := lexer.currentPos - lexer.lastWidth | ||||
| 	nextRune := lexer.next() | ||||
| 	var t token | ||||
| 	if nextRune == '?' { | ||||
| 		t = token{ | ||||
| 			tokenType: tFilter, | ||||
| 			value:     "[?", | ||||
| 			position:  start, | ||||
| 			length:    2, | ||||
| 		} | ||||
| 	} else if nextRune == ']' { | ||||
| 		t = token{ | ||||
| 			tokenType: tFlatten, | ||||
| 			value:     "[]", | ||||
| 			position:  start, | ||||
| 			length:    2, | ||||
| 		} | ||||
| 	} else { | ||||
| 		t = token{ | ||||
| 			tokenType: tLbracket, | ||||
| 			value:     "[", | ||||
| 			position:  start, | ||||
| 			length:    1, | ||||
| 		} | ||||
| 		lexer.back() | ||||
| 	} | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| func (lexer *Lexer) consumeQuotedIdentifier() (token, error) { | ||||
| 	start := lexer.currentPos | ||||
| 	value, err := lexer.consumeUntil('"') | ||||
| 	if err != nil { | ||||
| 		return token{}, err | ||||
| 	} | ||||
| 	var decoded string | ||||
| 	asJSON := []byte("\"" + value + "\"") | ||||
| 	if err := json.Unmarshal([]byte(asJSON), &decoded); err != nil { | ||||
| 		return token{}, err | ||||
| 	} | ||||
| 	return token{ | ||||
| 		tokenType: tQuotedIdentifier, | ||||
| 		value:     decoded, | ||||
| 		position:  start - 1, | ||||
| 		length:    len(decoded), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (lexer *Lexer) consumeUnquotedIdentifier() token { | ||||
| 	// Consume runes until we reach the end of an unquoted | ||||
| 	// identifier. | ||||
| 	start := lexer.currentPos - lexer.lastWidth | ||||
| 	for { | ||||
| 		r := lexer.next() | ||||
| 		if r < 0 || r > 128 || identifierTrailingBits[uint64(r)/64]&(1<<(uint64(r)%64)) == 0 { | ||||
| 			lexer.back() | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	value := lexer.expression[start:lexer.currentPos] | ||||
| 	return token{ | ||||
| 		tokenType: tUnquotedIdentifier, | ||||
| 		value:     value, | ||||
| 		position:  start, | ||||
| 		length:    lexer.currentPos - start, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (lexer *Lexer) consumeNumber() token { | ||||
| 	// Consume runes until we reach something that's not a number. | ||||
| 	start := lexer.currentPos - lexer.lastWidth | ||||
| 	for { | ||||
| 		r := lexer.next() | ||||
| 		if r < '0' || r > '9' { | ||||
| 			lexer.back() | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	value := lexer.expression[start:lexer.currentPos] | ||||
| 	return token{ | ||||
| 		tokenType: tNumber, | ||||
| 		value:     value, | ||||
| 		position:  start, | ||||
| 		length:    lexer.currentPos - start, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										161
									
								
								vendor/github.com/jmespath/go-jmespath/lexer_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								vendor/github.com/jmespath/go-jmespath/lexer_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| var lexingTests = []struct { | ||||
| 	expression string | ||||
| 	expected   []token | ||||
| }{ | ||||
| 	{"*", []token{{tStar, "*", 0, 1}}}, | ||||
| 	{".", []token{{tDot, ".", 0, 1}}}, | ||||
| 	{"[?", []token{{tFilter, "[?", 0, 2}}}, | ||||
| 	{"[]", []token{{tFlatten, "[]", 0, 2}}}, | ||||
| 	{"(", []token{{tLparen, "(", 0, 1}}}, | ||||
| 	{")", []token{{tRparen, ")", 0, 1}}}, | ||||
| 	{"[", []token{{tLbracket, "[", 0, 1}}}, | ||||
| 	{"]", []token{{tRbracket, "]", 0, 1}}}, | ||||
| 	{"{", []token{{tLbrace, "{", 0, 1}}}, | ||||
| 	{"}", []token{{tRbrace, "}", 0, 1}}}, | ||||
| 	{"||", []token{{tOr, "||", 0, 2}}}, | ||||
| 	{"|", []token{{tPipe, "|", 0, 1}}}, | ||||
| 	{"29", []token{{tNumber, "29", 0, 2}}}, | ||||
| 	{"2", []token{{tNumber, "2", 0, 1}}}, | ||||
| 	{"0", []token{{tNumber, "0", 0, 1}}}, | ||||
| 	{"-20", []token{{tNumber, "-20", 0, 3}}}, | ||||
| 	{"foo", []token{{tUnquotedIdentifier, "foo", 0, 3}}}, | ||||
| 	{`"bar"`, []token{{tQuotedIdentifier, "bar", 0, 3}}}, | ||||
| 	// Escaping the delimiter | ||||
| 	{`"bar\"baz"`, []token{{tQuotedIdentifier, `bar"baz`, 0, 7}}}, | ||||
| 	{",", []token{{tComma, ",", 0, 1}}}, | ||||
| 	{":", []token{{tColon, ":", 0, 1}}}, | ||||
| 	{"<", []token{{tLT, "<", 0, 1}}}, | ||||
| 	{"<=", []token{{tLTE, "<=", 0, 2}}}, | ||||
| 	{">", []token{{tGT, ">", 0, 1}}}, | ||||
| 	{">=", []token{{tGTE, ">=", 0, 2}}}, | ||||
| 	{"==", []token{{tEQ, "==", 0, 2}}}, | ||||
| 	{"!=", []token{{tNE, "!=", 0, 2}}}, | ||||
| 	{"`[0, 1, 2]`", []token{{tJSONLiteral, "[0, 1, 2]", 1, 9}}}, | ||||
| 	{"'foo'", []token{{tStringLiteral, "foo", 1, 3}}}, | ||||
| 	{"'a'", []token{{tStringLiteral, "a", 1, 1}}}, | ||||
| 	{`'foo\'bar'`, []token{{tStringLiteral, "foo'bar", 1, 7}}}, | ||||
| 	{"@", []token{{tCurrent, "@", 0, 1}}}, | ||||
| 	{"&", []token{{tExpref, "&", 0, 1}}}, | ||||
| 	// Quoted identifier unicode escape sequences | ||||
| 	{`"\u2713"`, []token{{tQuotedIdentifier, "✓", 0, 3}}}, | ||||
| 	{`"\\"`, []token{{tQuotedIdentifier, `\`, 0, 1}}}, | ||||
| 	{"`\"foo\"`", []token{{tJSONLiteral, "\"foo\"", 1, 5}}}, | ||||
| 	// Combinations of tokens. | ||||
| 	{"foo.bar", []token{ | ||||
| 		{tUnquotedIdentifier, "foo", 0, 3}, | ||||
| 		{tDot, ".", 3, 1}, | ||||
| 		{tUnquotedIdentifier, "bar", 4, 3}, | ||||
| 	}}, | ||||
| 	{"foo[0]", []token{ | ||||
| 		{tUnquotedIdentifier, "foo", 0, 3}, | ||||
| 		{tLbracket, "[", 3, 1}, | ||||
| 		{tNumber, "0", 4, 1}, | ||||
| 		{tRbracket, "]", 5, 1}, | ||||
| 	}}, | ||||
| 	{"foo[?a<b]", []token{ | ||||
| 		{tUnquotedIdentifier, "foo", 0, 3}, | ||||
| 		{tFilter, "[?", 3, 2}, | ||||
| 		{tUnquotedIdentifier, "a", 5, 1}, | ||||
| 		{tLT, "<", 6, 1}, | ||||
| 		{tUnquotedIdentifier, "b", 7, 1}, | ||||
| 		{tRbracket, "]", 8, 1}, | ||||
| 	}}, | ||||
| } | ||||
|  | ||||
| func TestCanLexTokens(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	lexer := NewLexer() | ||||
| 	for _, tt := range lexingTests { | ||||
| 		tokens, err := lexer.tokenize(tt.expression) | ||||
| 		if assert.Nil(err) { | ||||
| 			errMsg := fmt.Sprintf("Mismatch expected number of tokens: (expected: %s, actual: %s)", | ||||
| 				tt.expected, tokens) | ||||
| 			tt.expected = append(tt.expected, token{tEOF, "", len(tt.expression), 0}) | ||||
| 			if assert.Equal(len(tt.expected), len(tokens), errMsg) { | ||||
| 				for i, token := range tokens { | ||||
| 					expected := tt.expected[i] | ||||
| 					assert.Equal(expected, token, "Token not equal") | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var lexingErrorTests = []struct { | ||||
| 	expression string | ||||
| 	msg        string | ||||
| }{ | ||||
| 	{"'foo", "Missing closing single quote"}, | ||||
| 	{"[?foo==bar?]", "Unknown char '?'"}, | ||||
| } | ||||
|  | ||||
| func TestLexingErrors(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	lexer := NewLexer() | ||||
| 	for _, tt := range lexingErrorTests { | ||||
| 		_, err := lexer.tokenize(tt.expression) | ||||
| 		assert.NotNil(err, fmt.Sprintf("Expected lexing error: %s", tt.msg)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var exprIdentifier = "abcdefghijklmnopqrstuvwxyz" | ||||
| var exprSubexpr = "abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz" | ||||
| var deeplyNested50 = "j49.j48.j47.j46.j45.j44.j43.j42.j41.j40.j39.j38.j37.j36.j35.j34.j33.j32.j31.j30.j29.j28.j27.j26.j25.j24.j23.j22.j21.j20.j19.j18.j17.j16.j15.j14.j13.j12.j11.j10.j9.j8.j7.j6.j5.j4.j3.j2.j1.j0" | ||||
| var deeplyNested50Pipe = "j49|j48|j47|j46|j45|j44|j43|j42|j41|j40|j39|j38|j37|j36|j35|j34|j33|j32|j31|j30|j29|j28|j27|j26|j25|j24|j23|j22|j21|j20|j19|j18|j17|j16|j15|j14|j13|j12|j11|j10|j9|j8|j7|j6|j5|j4|j3|j2|j1|j0" | ||||
| var deeplyNested50Index = "[49][48][47][46][45][44][43][42][41][40][39][38][37][36][35][34][33][32][31][30][29][28][27][26][25][24][23][22][21][20][19][18][17][16][15][14][13][12][11][10][9][8][7][6][5][4][3][2][1][0]" | ||||
| var deepProjection104 = "a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*]" | ||||
| var exprQuotedIdentifier = `"abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz"` | ||||
| var quotedIdentifierEscapes = `"\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t"` | ||||
| var rawStringLiteral = `'abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz'` | ||||
|  | ||||
| func BenchmarkLexIdentifier(b *testing.B) { | ||||
| 	runLexBenchmark(b, exprIdentifier) | ||||
| } | ||||
|  | ||||
| func BenchmarkLexSubexpression(b *testing.B) { | ||||
| 	runLexBenchmark(b, exprSubexpr) | ||||
| } | ||||
|  | ||||
| func BenchmarkLexDeeplyNested50(b *testing.B) { | ||||
| 	runLexBenchmark(b, deeplyNested50) | ||||
| } | ||||
|  | ||||
| func BenchmarkLexDeepNested50Pipe(b *testing.B) { | ||||
| 	runLexBenchmark(b, deeplyNested50Pipe) | ||||
| } | ||||
|  | ||||
| func BenchmarkLexDeepNested50Index(b *testing.B) { | ||||
| 	runLexBenchmark(b, deeplyNested50Index) | ||||
| } | ||||
|  | ||||
| func BenchmarkLexQuotedIdentifier(b *testing.B) { | ||||
| 	runLexBenchmark(b, exprQuotedIdentifier) | ||||
| } | ||||
|  | ||||
| func BenchmarkLexQuotedIdentifierEscapes(b *testing.B) { | ||||
| 	runLexBenchmark(b, quotedIdentifierEscapes) | ||||
| } | ||||
|  | ||||
| func BenchmarkLexRawStringLiteral(b *testing.B) { | ||||
| 	runLexBenchmark(b, rawStringLiteral) | ||||
| } | ||||
|  | ||||
| func BenchmarkLexDeepProjection104(b *testing.B) { | ||||
| 	runLexBenchmark(b, deepProjection104) | ||||
| } | ||||
|  | ||||
| func runLexBenchmark(b *testing.B, expression string) { | ||||
| 	lexer := NewLexer() | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		lexer.tokenize(expression) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										603
									
								
								vendor/github.com/jmespath/go-jmespath/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										603
									
								
								vendor/github.com/jmespath/go-jmespath/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,603 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| type astNodeType int | ||||
|  | ||||
| //go:generate stringer -type astNodeType | ||||
| const ( | ||||
| 	ASTEmpty astNodeType = iota | ||||
| 	ASTComparator | ||||
| 	ASTCurrentNode | ||||
| 	ASTExpRef | ||||
| 	ASTFunctionExpression | ||||
| 	ASTField | ||||
| 	ASTFilterProjection | ||||
| 	ASTFlatten | ||||
| 	ASTIdentity | ||||
| 	ASTIndex | ||||
| 	ASTIndexExpression | ||||
| 	ASTKeyValPair | ||||
| 	ASTLiteral | ||||
| 	ASTMultiSelectHash | ||||
| 	ASTMultiSelectList | ||||
| 	ASTOrExpression | ||||
| 	ASTAndExpression | ||||
| 	ASTNotExpression | ||||
| 	ASTPipe | ||||
| 	ASTProjection | ||||
| 	ASTSubexpression | ||||
| 	ASTSlice | ||||
| 	ASTValueProjection | ||||
| ) | ||||
|  | ||||
| // ASTNode represents the abstract syntax tree of a JMESPath expression. | ||||
| type ASTNode struct { | ||||
| 	nodeType astNodeType | ||||
| 	value    interface{} | ||||
| 	children []ASTNode | ||||
| } | ||||
|  | ||||
| func (node ASTNode) String() string { | ||||
| 	return node.PrettyPrint(0) | ||||
| } | ||||
|  | ||||
| // PrettyPrint will pretty print the parsed AST. | ||||
| // The AST is an implementation detail and this pretty print | ||||
| // function is provided as a convenience method to help with | ||||
| // debugging.  You should not rely on its output as the internal | ||||
| // structure of the AST may change at any time. | ||||
| func (node ASTNode) PrettyPrint(indent int) string { | ||||
| 	spaces := strings.Repeat(" ", indent) | ||||
| 	output := fmt.Sprintf("%s%s {\n", spaces, node.nodeType) | ||||
| 	nextIndent := indent + 2 | ||||
| 	if node.value != nil { | ||||
| 		if converted, ok := node.value.(fmt.Stringer); ok { | ||||
| 			// Account for things like comparator nodes | ||||
| 			// that are enums with a String() method. | ||||
| 			output += fmt.Sprintf("%svalue: %s\n", strings.Repeat(" ", nextIndent), converted.String()) | ||||
| 		} else { | ||||
| 			output += fmt.Sprintf("%svalue: %#v\n", strings.Repeat(" ", nextIndent), node.value) | ||||
| 		} | ||||
| 	} | ||||
| 	lastIndex := len(node.children) | ||||
| 	if lastIndex > 0 { | ||||
| 		output += fmt.Sprintf("%schildren: {\n", strings.Repeat(" ", nextIndent)) | ||||
| 		childIndent := nextIndent + 2 | ||||
| 		for _, elem := range node.children { | ||||
| 			output += elem.PrettyPrint(childIndent) | ||||
| 		} | ||||
| 	} | ||||
| 	output += fmt.Sprintf("%s}\n", spaces) | ||||
| 	return output | ||||
| } | ||||
|  | ||||
| var bindingPowers = map[tokType]int{ | ||||
| 	tEOF:                0, | ||||
| 	tUnquotedIdentifier: 0, | ||||
| 	tQuotedIdentifier:   0, | ||||
| 	tRbracket:           0, | ||||
| 	tRparen:             0, | ||||
| 	tComma:              0, | ||||
| 	tRbrace:             0, | ||||
| 	tNumber:             0, | ||||
| 	tCurrent:            0, | ||||
| 	tExpref:             0, | ||||
| 	tColon:              0, | ||||
| 	tPipe:               1, | ||||
| 	tOr:                 2, | ||||
| 	tAnd:                3, | ||||
| 	tEQ:                 5, | ||||
| 	tLT:                 5, | ||||
| 	tLTE:                5, | ||||
| 	tGT:                 5, | ||||
| 	tGTE:                5, | ||||
| 	tNE:                 5, | ||||
| 	tFlatten:            9, | ||||
| 	tStar:               20, | ||||
| 	tFilter:             21, | ||||
| 	tDot:                40, | ||||
| 	tNot:                45, | ||||
| 	tLbrace:             50, | ||||
| 	tLbracket:           55, | ||||
| 	tLparen:             60, | ||||
| } | ||||
|  | ||||
| // Parser holds state about the current expression being parsed. | ||||
| type Parser struct { | ||||
| 	expression string | ||||
| 	tokens     []token | ||||
| 	index      int | ||||
| } | ||||
|  | ||||
| // NewParser creates a new JMESPath parser. | ||||
| func NewParser() *Parser { | ||||
| 	p := Parser{} | ||||
| 	return &p | ||||
| } | ||||
|  | ||||
| // Parse will compile a JMESPath expression. | ||||
| func (p *Parser) Parse(expression string) (ASTNode, error) { | ||||
| 	lexer := NewLexer() | ||||
| 	p.expression = expression | ||||
| 	p.index = 0 | ||||
| 	tokens, err := lexer.tokenize(expression) | ||||
| 	if err != nil { | ||||
| 		return ASTNode{}, err | ||||
| 	} | ||||
| 	p.tokens = tokens | ||||
| 	parsed, err := p.parseExpression(0) | ||||
| 	if err != nil { | ||||
| 		return ASTNode{}, err | ||||
| 	} | ||||
| 	if p.current() != tEOF { | ||||
| 		return ASTNode{}, p.syntaxError(fmt.Sprintf( | ||||
| 			"Unexpected token at the end of the expresssion: %s", p.current())) | ||||
| 	} | ||||
| 	return parsed, nil | ||||
| } | ||||
|  | ||||
| func (p *Parser) parseExpression(bindingPower int) (ASTNode, error) { | ||||
| 	var err error | ||||
| 	leftToken := p.lookaheadToken(0) | ||||
| 	p.advance() | ||||
| 	leftNode, err := p.nud(leftToken) | ||||
| 	if err != nil { | ||||
| 		return ASTNode{}, err | ||||
| 	} | ||||
| 	currentToken := p.current() | ||||
| 	for bindingPower < bindingPowers[currentToken] { | ||||
| 		p.advance() | ||||
| 		leftNode, err = p.led(currentToken, leftNode) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		currentToken = p.current() | ||||
| 	} | ||||
| 	return leftNode, nil | ||||
| } | ||||
|  | ||||
| func (p *Parser) parseIndexExpression() (ASTNode, error) { | ||||
| 	if p.lookahead(0) == tColon || p.lookahead(1) == tColon { | ||||
| 		return p.parseSliceExpression() | ||||
| 	} | ||||
| 	indexStr := p.lookaheadToken(0).value | ||||
| 	parsedInt, err := strconv.Atoi(indexStr) | ||||
| 	if err != nil { | ||||
| 		return ASTNode{}, err | ||||
| 	} | ||||
| 	indexNode := ASTNode{nodeType: ASTIndex, value: parsedInt} | ||||
| 	p.advance() | ||||
| 	if err := p.match(tRbracket); err != nil { | ||||
| 		return ASTNode{}, err | ||||
| 	} | ||||
| 	return indexNode, nil | ||||
| } | ||||
|  | ||||
| func (p *Parser) parseSliceExpression() (ASTNode, error) { | ||||
| 	parts := []*int{nil, nil, nil} | ||||
| 	index := 0 | ||||
| 	current := p.current() | ||||
| 	for current != tRbracket && index < 3 { | ||||
| 		if current == tColon { | ||||
| 			index++ | ||||
| 			p.advance() | ||||
| 		} else if current == tNumber { | ||||
| 			parsedInt, err := strconv.Atoi(p.lookaheadToken(0).value) | ||||
| 			if err != nil { | ||||
| 				return ASTNode{}, err | ||||
| 			} | ||||
| 			parts[index] = &parsedInt | ||||
| 			p.advance() | ||||
| 		} else { | ||||
| 			return ASTNode{}, p.syntaxError( | ||||
| 				"Expected tColon or tNumber" + ", received: " + p.current().String()) | ||||
| 		} | ||||
| 		current = p.current() | ||||
| 	} | ||||
| 	if err := p.match(tRbracket); err != nil { | ||||
| 		return ASTNode{}, err | ||||
| 	} | ||||
| 	return ASTNode{ | ||||
| 		nodeType: ASTSlice, | ||||
| 		value:    parts, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (p *Parser) match(tokenType tokType) error { | ||||
| 	if p.current() == tokenType { | ||||
| 		p.advance() | ||||
| 		return nil | ||||
| 	} | ||||
| 	return p.syntaxError("Expected " + tokenType.String() + ", received: " + p.current().String()) | ||||
| } | ||||
|  | ||||
| func (p *Parser) led(tokenType tokType, node ASTNode) (ASTNode, error) { | ||||
| 	switch tokenType { | ||||
| 	case tDot: | ||||
| 		if p.current() != tStar { | ||||
| 			right, err := p.parseDotRHS(bindingPowers[tDot]) | ||||
| 			return ASTNode{ | ||||
| 				nodeType: ASTSubexpression, | ||||
| 				children: []ASTNode{node, right}, | ||||
| 			}, err | ||||
| 		} | ||||
| 		p.advance() | ||||
| 		right, err := p.parseProjectionRHS(bindingPowers[tDot]) | ||||
| 		return ASTNode{ | ||||
| 			nodeType: ASTValueProjection, | ||||
| 			children: []ASTNode{node, right}, | ||||
| 		}, err | ||||
| 	case tPipe: | ||||
| 		right, err := p.parseExpression(bindingPowers[tPipe]) | ||||
| 		return ASTNode{nodeType: ASTPipe, children: []ASTNode{node, right}}, err | ||||
| 	case tOr: | ||||
| 		right, err := p.parseExpression(bindingPowers[tOr]) | ||||
| 		return ASTNode{nodeType: ASTOrExpression, children: []ASTNode{node, right}}, err | ||||
| 	case tAnd: | ||||
| 		right, err := p.parseExpression(bindingPowers[tAnd]) | ||||
| 		return ASTNode{nodeType: ASTAndExpression, children: []ASTNode{node, right}}, err | ||||
| 	case tLparen: | ||||
| 		name := node.value | ||||
| 		var args []ASTNode | ||||
| 		for p.current() != tRparen { | ||||
| 			expression, err := p.parseExpression(0) | ||||
| 			if err != nil { | ||||
| 				return ASTNode{}, err | ||||
| 			} | ||||
| 			if p.current() == tComma { | ||||
| 				if err := p.match(tComma); err != nil { | ||||
| 					return ASTNode{}, err | ||||
| 				} | ||||
| 			} | ||||
| 			args = append(args, expression) | ||||
| 		} | ||||
| 		if err := p.match(tRparen); err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return ASTNode{ | ||||
| 			nodeType: ASTFunctionExpression, | ||||
| 			value:    name, | ||||
| 			children: args, | ||||
| 		}, nil | ||||
| 	case tFilter: | ||||
| 		return p.parseFilter(node) | ||||
| 	case tFlatten: | ||||
| 		left := ASTNode{nodeType: ASTFlatten, children: []ASTNode{node}} | ||||
| 		right, err := p.parseProjectionRHS(bindingPowers[tFlatten]) | ||||
| 		return ASTNode{ | ||||
| 			nodeType: ASTProjection, | ||||
| 			children: []ASTNode{left, right}, | ||||
| 		}, err | ||||
| 	case tEQ, tNE, tGT, tGTE, tLT, tLTE: | ||||
| 		right, err := p.parseExpression(bindingPowers[tokenType]) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return ASTNode{ | ||||
| 			nodeType: ASTComparator, | ||||
| 			value:    tokenType, | ||||
| 			children: []ASTNode{node, right}, | ||||
| 		}, nil | ||||
| 	case tLbracket: | ||||
| 		tokenType := p.current() | ||||
| 		var right ASTNode | ||||
| 		var err error | ||||
| 		if tokenType == tNumber || tokenType == tColon { | ||||
| 			right, err = p.parseIndexExpression() | ||||
| 			if err != nil { | ||||
| 				return ASTNode{}, err | ||||
| 			} | ||||
| 			return p.projectIfSlice(node, right) | ||||
| 		} | ||||
| 		// Otherwise this is a projection. | ||||
| 		if err := p.match(tStar); err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		if err := p.match(tRbracket); err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		right, err = p.parseProjectionRHS(bindingPowers[tStar]) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return ASTNode{ | ||||
| 			nodeType: ASTProjection, | ||||
| 			children: []ASTNode{node, right}, | ||||
| 		}, nil | ||||
| 	} | ||||
| 	return ASTNode{}, p.syntaxError("Unexpected token: " + tokenType.String()) | ||||
| } | ||||
|  | ||||
| func (p *Parser) nud(token token) (ASTNode, error) { | ||||
| 	switch token.tokenType { | ||||
| 	case tJSONLiteral: | ||||
| 		var parsed interface{} | ||||
| 		err := json.Unmarshal([]byte(token.value), &parsed) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return ASTNode{nodeType: ASTLiteral, value: parsed}, nil | ||||
| 	case tStringLiteral: | ||||
| 		return ASTNode{nodeType: ASTLiteral, value: token.value}, nil | ||||
| 	case tUnquotedIdentifier: | ||||
| 		return ASTNode{ | ||||
| 			nodeType: ASTField, | ||||
| 			value:    token.value, | ||||
| 		}, nil | ||||
| 	case tQuotedIdentifier: | ||||
| 		node := ASTNode{nodeType: ASTField, value: token.value} | ||||
| 		if p.current() == tLparen { | ||||
| 			return ASTNode{}, p.syntaxErrorToken("Can't have quoted identifier as function name.", token) | ||||
| 		} | ||||
| 		return node, nil | ||||
| 	case tStar: | ||||
| 		left := ASTNode{nodeType: ASTIdentity} | ||||
| 		var right ASTNode | ||||
| 		var err error | ||||
| 		if p.current() == tRbracket { | ||||
| 			right = ASTNode{nodeType: ASTIdentity} | ||||
| 		} else { | ||||
| 			right, err = p.parseProjectionRHS(bindingPowers[tStar]) | ||||
| 		} | ||||
| 		return ASTNode{nodeType: ASTValueProjection, children: []ASTNode{left, right}}, err | ||||
| 	case tFilter: | ||||
| 		return p.parseFilter(ASTNode{nodeType: ASTIdentity}) | ||||
| 	case tLbrace: | ||||
| 		return p.parseMultiSelectHash() | ||||
| 	case tFlatten: | ||||
| 		left := ASTNode{ | ||||
| 			nodeType: ASTFlatten, | ||||
| 			children: []ASTNode{{nodeType: ASTIdentity}}, | ||||
| 		} | ||||
| 		right, err := p.parseProjectionRHS(bindingPowers[tFlatten]) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return ASTNode{nodeType: ASTProjection, children: []ASTNode{left, right}}, nil | ||||
| 	case tLbracket: | ||||
| 		tokenType := p.current() | ||||
| 		//var right ASTNode | ||||
| 		if tokenType == tNumber || tokenType == tColon { | ||||
| 			right, err := p.parseIndexExpression() | ||||
| 			if err != nil { | ||||
| 				return ASTNode{}, nil | ||||
| 			} | ||||
| 			return p.projectIfSlice(ASTNode{nodeType: ASTIdentity}, right) | ||||
| 		} else if tokenType == tStar && p.lookahead(1) == tRbracket { | ||||
| 			p.advance() | ||||
| 			p.advance() | ||||
| 			right, err := p.parseProjectionRHS(bindingPowers[tStar]) | ||||
| 			if err != nil { | ||||
| 				return ASTNode{}, err | ||||
| 			} | ||||
| 			return ASTNode{ | ||||
| 				nodeType: ASTProjection, | ||||
| 				children: []ASTNode{{nodeType: ASTIdentity}, right}, | ||||
| 			}, nil | ||||
| 		} else { | ||||
| 			return p.parseMultiSelectList() | ||||
| 		} | ||||
| 	case tCurrent: | ||||
| 		return ASTNode{nodeType: ASTCurrentNode}, nil | ||||
| 	case tExpref: | ||||
| 		expression, err := p.parseExpression(bindingPowers[tExpref]) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return ASTNode{nodeType: ASTExpRef, children: []ASTNode{expression}}, nil | ||||
| 	case tNot: | ||||
| 		expression, err := p.parseExpression(bindingPowers[tNot]) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return ASTNode{nodeType: ASTNotExpression, children: []ASTNode{expression}}, nil | ||||
| 	case tLparen: | ||||
| 		expression, err := p.parseExpression(0) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		if err := p.match(tRparen); err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return expression, nil | ||||
| 	case tEOF: | ||||
| 		return ASTNode{}, p.syntaxErrorToken("Incomplete expression", token) | ||||
| 	} | ||||
|  | ||||
| 	return ASTNode{}, p.syntaxErrorToken("Invalid token: "+token.tokenType.String(), token) | ||||
| } | ||||
|  | ||||
| func (p *Parser) parseMultiSelectList() (ASTNode, error) { | ||||
| 	var expressions []ASTNode | ||||
| 	for { | ||||
| 		expression, err := p.parseExpression(0) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		expressions = append(expressions, expression) | ||||
| 		if p.current() == tRbracket { | ||||
| 			break | ||||
| 		} | ||||
| 		err = p.match(tComma) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 	} | ||||
| 	err := p.match(tRbracket) | ||||
| 	if err != nil { | ||||
| 		return ASTNode{}, err | ||||
| 	} | ||||
| 	return ASTNode{ | ||||
| 		nodeType: ASTMultiSelectList, | ||||
| 		children: expressions, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (p *Parser) parseMultiSelectHash() (ASTNode, error) { | ||||
| 	var children []ASTNode | ||||
| 	for { | ||||
| 		keyToken := p.lookaheadToken(0) | ||||
| 		if err := p.match(tUnquotedIdentifier); err != nil { | ||||
| 			if err := p.match(tQuotedIdentifier); err != nil { | ||||
| 				return ASTNode{}, p.syntaxError("Expected tQuotedIdentifier or tUnquotedIdentifier") | ||||
| 			} | ||||
| 		} | ||||
| 		keyName := keyToken.value | ||||
| 		err := p.match(tColon) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		value, err := p.parseExpression(0) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		node := ASTNode{ | ||||
| 			nodeType: ASTKeyValPair, | ||||
| 			value:    keyName, | ||||
| 			children: []ASTNode{value}, | ||||
| 		} | ||||
| 		children = append(children, node) | ||||
| 		if p.current() == tComma { | ||||
| 			err := p.match(tComma) | ||||
| 			if err != nil { | ||||
| 				return ASTNode{}, nil | ||||
| 			} | ||||
| 		} else if p.current() == tRbrace { | ||||
| 			err := p.match(tRbrace) | ||||
| 			if err != nil { | ||||
| 				return ASTNode{}, nil | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return ASTNode{ | ||||
| 		nodeType: ASTMultiSelectHash, | ||||
| 		children: children, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (p *Parser) projectIfSlice(left ASTNode, right ASTNode) (ASTNode, error) { | ||||
| 	indexExpr := ASTNode{ | ||||
| 		nodeType: ASTIndexExpression, | ||||
| 		children: []ASTNode{left, right}, | ||||
| 	} | ||||
| 	if right.nodeType == ASTSlice { | ||||
| 		right, err := p.parseProjectionRHS(bindingPowers[tStar]) | ||||
| 		return ASTNode{ | ||||
| 			nodeType: ASTProjection, | ||||
| 			children: []ASTNode{indexExpr, right}, | ||||
| 		}, err | ||||
| 	} | ||||
| 	return indexExpr, nil | ||||
| } | ||||
| func (p *Parser) parseFilter(node ASTNode) (ASTNode, error) { | ||||
| 	var right, condition ASTNode | ||||
| 	var err error | ||||
| 	condition, err = p.parseExpression(0) | ||||
| 	if err != nil { | ||||
| 		return ASTNode{}, err | ||||
| 	} | ||||
| 	if err := p.match(tRbracket); err != nil { | ||||
| 		return ASTNode{}, err | ||||
| 	} | ||||
| 	if p.current() == tFlatten { | ||||
| 		right = ASTNode{nodeType: ASTIdentity} | ||||
| 	} else { | ||||
| 		right, err = p.parseProjectionRHS(bindingPowers[tFilter]) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ASTNode{ | ||||
| 		nodeType: ASTFilterProjection, | ||||
| 		children: []ASTNode{node, right, condition}, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (p *Parser) parseDotRHS(bindingPower int) (ASTNode, error) { | ||||
| 	lookahead := p.current() | ||||
| 	if tokensOneOf([]tokType{tQuotedIdentifier, tUnquotedIdentifier, tStar}, lookahead) { | ||||
| 		return p.parseExpression(bindingPower) | ||||
| 	} else if lookahead == tLbracket { | ||||
| 		if err := p.match(tLbracket); err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return p.parseMultiSelectList() | ||||
| 	} else if lookahead == tLbrace { | ||||
| 		if err := p.match(tLbrace); err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return p.parseMultiSelectHash() | ||||
| 	} | ||||
| 	return ASTNode{}, p.syntaxError("Expected identifier, lbracket, or lbrace") | ||||
| } | ||||
|  | ||||
| func (p *Parser) parseProjectionRHS(bindingPower int) (ASTNode, error) { | ||||
| 	current := p.current() | ||||
| 	if bindingPowers[current] < 10 { | ||||
| 		return ASTNode{nodeType: ASTIdentity}, nil | ||||
| 	} else if current == tLbracket { | ||||
| 		return p.parseExpression(bindingPower) | ||||
| 	} else if current == tFilter { | ||||
| 		return p.parseExpression(bindingPower) | ||||
| 	} else if current == tDot { | ||||
| 		err := p.match(tDot) | ||||
| 		if err != nil { | ||||
| 			return ASTNode{}, err | ||||
| 		} | ||||
| 		return p.parseDotRHS(bindingPower) | ||||
| 	} else { | ||||
| 		return ASTNode{}, p.syntaxError("Error") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (p *Parser) lookahead(number int) tokType { | ||||
| 	return p.lookaheadToken(number).tokenType | ||||
| } | ||||
|  | ||||
| func (p *Parser) current() tokType { | ||||
| 	return p.lookahead(0) | ||||
| } | ||||
|  | ||||
| func (p *Parser) lookaheadToken(number int) token { | ||||
| 	return p.tokens[p.index+number] | ||||
| } | ||||
|  | ||||
| func (p *Parser) advance() { | ||||
| 	p.index++ | ||||
| } | ||||
|  | ||||
| func tokensOneOf(elements []tokType, token tokType) bool { | ||||
| 	for _, elem := range elements { | ||||
| 		if elem == token { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (p *Parser) syntaxError(msg string) SyntaxError { | ||||
| 	return SyntaxError{ | ||||
| 		msg:        msg, | ||||
| 		Expression: p.expression, | ||||
| 		Offset:     p.lookaheadToken(0).position, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Create a SyntaxError based on the provided token. | ||||
| // This differs from syntaxError() which creates a SyntaxError | ||||
| // based on the current lookahead token. | ||||
| func (p *Parser) syntaxErrorToken(msg string, t token) SyntaxError { | ||||
| 	return SyntaxError{ | ||||
| 		msg:        msg, | ||||
| 		Expression: p.expression, | ||||
| 		Offset:     t.position, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										136
									
								
								vendor/github.com/jmespath/go-jmespath/parser_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/jmespath/go-jmespath/parser_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| var parsingErrorTests = []struct { | ||||
| 	expression string | ||||
| 	msg        string | ||||
| }{ | ||||
| 	{"foo.", "Incopmlete expression"}, | ||||
| 	{"[foo", "Incopmlete expression"}, | ||||
| 	{"]", "Invalid"}, | ||||
| 	{")", "Invalid"}, | ||||
| 	{"}", "Invalid"}, | ||||
| 	{"foo..bar", "Invalid"}, | ||||
| 	{`foo."bar`, "Forwards lexer errors"}, | ||||
| 	{`{foo: bar`, "Incomplete expression"}, | ||||
| 	{`{foo bar}`, "Invalid"}, | ||||
| 	{`[foo bar]`, "Invalid"}, | ||||
| 	{`foo@`, "Invalid"}, | ||||
| 	{`&&&&&&&&&&&&t(`, "Invalid"}, | ||||
| 	{`[*][`, "Invalid"}, | ||||
| } | ||||
|  | ||||
| func TestParsingErrors(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	parser := NewParser() | ||||
| 	for _, tt := range parsingErrorTests { | ||||
| 		_, err := parser.Parse(tt.expression) | ||||
| 		assert.NotNil(err, fmt.Sprintf("Expected parsing error: %s, for expression: %s", tt.msg, tt.expression)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var prettyPrinted = `ASTProjection { | ||||
|   children: { | ||||
|     ASTField { | ||||
|       value: "foo" | ||||
|     } | ||||
|     ASTSubexpression { | ||||
|       children: { | ||||
|         ASTSubexpression { | ||||
|           children: { | ||||
|             ASTField { | ||||
|               value: "bar" | ||||
|             } | ||||
|             ASTField { | ||||
|               value: "baz" | ||||
|             } | ||||
|         } | ||||
|         ASTField { | ||||
|           value: "qux" | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ` | ||||
|  | ||||
| var prettyPrintedCompNode = `ASTFilterProjection { | ||||
|   children: { | ||||
|     ASTField { | ||||
|       value: "a" | ||||
|     } | ||||
|     ASTIdentity { | ||||
|     } | ||||
|     ASTComparator { | ||||
|       value: tLTE | ||||
|       children: { | ||||
|         ASTField { | ||||
|           value: "b" | ||||
|         } | ||||
|         ASTField { | ||||
|           value: "c" | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ` | ||||
|  | ||||
| func TestPrettyPrintedAST(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	parser := NewParser() | ||||
| 	parsed, _ := parser.Parse("foo[*].bar.baz.qux") | ||||
| 	assert.Equal(parsed.PrettyPrint(0), prettyPrinted) | ||||
| } | ||||
|  | ||||
| func TestPrettyPrintedCompNode(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	parser := NewParser() | ||||
| 	parsed, _ := parser.Parse("a[?b<=c]") | ||||
| 	assert.Equal(parsed.PrettyPrint(0), prettyPrintedCompNode) | ||||
| } | ||||
|  | ||||
| func BenchmarkParseIdentifier(b *testing.B) { | ||||
| 	runParseBenchmark(b, exprIdentifier) | ||||
| } | ||||
|  | ||||
| func BenchmarkParseSubexpression(b *testing.B) { | ||||
| 	runParseBenchmark(b, exprSubexpr) | ||||
| } | ||||
|  | ||||
| func BenchmarkParseDeeplyNested50(b *testing.B) { | ||||
| 	runParseBenchmark(b, deeplyNested50) | ||||
| } | ||||
|  | ||||
| func BenchmarkParseDeepNested50Pipe(b *testing.B) { | ||||
| 	runParseBenchmark(b, deeplyNested50Pipe) | ||||
| } | ||||
|  | ||||
| func BenchmarkParseDeepNested50Index(b *testing.B) { | ||||
| 	runParseBenchmark(b, deeplyNested50Index) | ||||
| } | ||||
|  | ||||
| func BenchmarkParseQuotedIdentifier(b *testing.B) { | ||||
| 	runParseBenchmark(b, exprQuotedIdentifier) | ||||
| } | ||||
|  | ||||
| func BenchmarkParseQuotedIdentifierEscapes(b *testing.B) { | ||||
| 	runParseBenchmark(b, quotedIdentifierEscapes) | ||||
| } | ||||
|  | ||||
| func BenchmarkParseRawStringLiteral(b *testing.B) { | ||||
| 	runParseBenchmark(b, rawStringLiteral) | ||||
| } | ||||
|  | ||||
| func BenchmarkParseDeepProjection104(b *testing.B) { | ||||
| 	runParseBenchmark(b, deepProjection104) | ||||
| } | ||||
|  | ||||
| func runParseBenchmark(b *testing.B, expression string) { | ||||
| 	parser := NewParser() | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		parser.Parse(expression) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										16
									
								
								vendor/github.com/jmespath/go-jmespath/toktype_string.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/jmespath/go-jmespath/toktype_string.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| // generated by stringer -type=tokType; DO NOT EDIT | ||||
|  | ||||
| package jmespath | ||||
|  | ||||
| import "fmt" | ||||
|  | ||||
| const _tokType_name = "tUnknowntStartDottFiltertFlattentLparentRparentLbrackettRbrackettLbracetRbracetOrtPipetNumbertUnquotedIdentifiertQuotedIdentifiertCommatColontLTtLTEtGTtGTEtEQtNEtJSONLiteraltStringLiteraltCurrenttExpreftAndtNottEOF" | ||||
|  | ||||
| var _tokType_index = [...]uint8{0, 8, 13, 17, 24, 32, 39, 46, 55, 64, 71, 78, 81, 86, 93, 112, 129, 135, 141, 144, 148, 151, 155, 158, 161, 173, 187, 195, 202, 206, 210, 214} | ||||
|  | ||||
| func (i tokType) String() string { | ||||
| 	if i < 0 || i >= tokType(len(_tokType_index)-1) { | ||||
| 		return fmt.Sprintf("tokType(%d)", i) | ||||
| 	} | ||||
| 	return _tokType_name[_tokType_index[i]:_tokType_index[i+1]] | ||||
| } | ||||
							
								
								
									
										185
									
								
								vendor/github.com/jmespath/go-jmespath/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								vendor/github.com/jmespath/go-jmespath/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,185 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"reflect" | ||||
| ) | ||||
|  | ||||
| // IsFalse determines if an object is false based on the JMESPath spec. | ||||
| // JMESPath defines false values to be any of: | ||||
| // - An empty string array, or hash. | ||||
| // - The boolean value false. | ||||
| // - nil | ||||
| func isFalse(value interface{}) bool { | ||||
| 	switch v := value.(type) { | ||||
| 	case bool: | ||||
| 		return !v | ||||
| 	case []interface{}: | ||||
| 		return len(v) == 0 | ||||
| 	case map[string]interface{}: | ||||
| 		return len(v) == 0 | ||||
| 	case string: | ||||
| 		return len(v) == 0 | ||||
| 	case nil: | ||||
| 		return true | ||||
| 	} | ||||
| 	// Try the reflection cases before returning false. | ||||
| 	rv := reflect.ValueOf(value) | ||||
| 	switch rv.Kind() { | ||||
| 	case reflect.Struct: | ||||
| 		// A struct type will never be false, even if | ||||
| 		// all of its values are the zero type. | ||||
| 		return false | ||||
| 	case reflect.Slice, reflect.Map: | ||||
| 		return rv.Len() == 0 | ||||
| 	case reflect.Ptr: | ||||
| 		if rv.IsNil() { | ||||
| 			return true | ||||
| 		} | ||||
| 		// If it's a pointer type, we'll try to deref the pointer | ||||
| 		// and evaluate the pointer value for isFalse. | ||||
| 		element := rv.Elem() | ||||
| 		return isFalse(element.Interface()) | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // ObjsEqual is a generic object equality check. | ||||
| // It will take two arbitrary objects and recursively determine | ||||
| // if they are equal. | ||||
| func objsEqual(left interface{}, right interface{}) bool { | ||||
| 	return reflect.DeepEqual(left, right) | ||||
| } | ||||
|  | ||||
| // SliceParam refers to a single part of a slice. | ||||
| // A slice consists of a start, a stop, and a step, similar to | ||||
| // python slices. | ||||
| type sliceParam struct { | ||||
| 	N         int | ||||
| 	Specified bool | ||||
| } | ||||
|  | ||||
| // Slice supports [start:stop:step] style slicing that's supported in JMESPath. | ||||
| func slice(slice []interface{}, parts []sliceParam) ([]interface{}, error) { | ||||
| 	computed, err := computeSliceParams(len(slice), parts) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	start, stop, step := computed[0], computed[1], computed[2] | ||||
| 	result := []interface{}{} | ||||
| 	if step > 0 { | ||||
| 		for i := start; i < stop; i += step { | ||||
| 			result = append(result, slice[i]) | ||||
| 		} | ||||
| 	} else { | ||||
| 		for i := start; i > stop; i += step { | ||||
| 			result = append(result, slice[i]) | ||||
| 		} | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| func computeSliceParams(length int, parts []sliceParam) ([]int, error) { | ||||
| 	var start, stop, step int | ||||
| 	if !parts[2].Specified { | ||||
| 		step = 1 | ||||
| 	} else if parts[2].N == 0 { | ||||
| 		return nil, errors.New("Invalid slice, step cannot be 0") | ||||
| 	} else { | ||||
| 		step = parts[2].N | ||||
| 	} | ||||
| 	var stepValueNegative bool | ||||
| 	if step < 0 { | ||||
| 		stepValueNegative = true | ||||
| 	} else { | ||||
| 		stepValueNegative = false | ||||
| 	} | ||||
|  | ||||
| 	if !parts[0].Specified { | ||||
| 		if stepValueNegative { | ||||
| 			start = length - 1 | ||||
| 		} else { | ||||
| 			start = 0 | ||||
| 		} | ||||
| 	} else { | ||||
| 		start = capSlice(length, parts[0].N, step) | ||||
| 	} | ||||
|  | ||||
| 	if !parts[1].Specified { | ||||
| 		if stepValueNegative { | ||||
| 			stop = -1 | ||||
| 		} else { | ||||
| 			stop = length | ||||
| 		} | ||||
| 	} else { | ||||
| 		stop = capSlice(length, parts[1].N, step) | ||||
| 	} | ||||
| 	return []int{start, stop, step}, nil | ||||
| } | ||||
|  | ||||
| func capSlice(length int, actual int, step int) int { | ||||
| 	if actual < 0 { | ||||
| 		actual += length | ||||
| 		if actual < 0 { | ||||
| 			if step < 0 { | ||||
| 				actual = -1 | ||||
| 			} else { | ||||
| 				actual = 0 | ||||
| 			} | ||||
| 		} | ||||
| 	} else if actual >= length { | ||||
| 		if step < 0 { | ||||
| 			actual = length - 1 | ||||
| 		} else { | ||||
| 			actual = length | ||||
| 		} | ||||
| 	} | ||||
| 	return actual | ||||
| } | ||||
|  | ||||
| // ToArrayNum converts an empty interface type to a slice of float64. | ||||
| // If any element in the array cannot be converted, then nil is returned | ||||
| // along with a second value of false. | ||||
| func toArrayNum(data interface{}) ([]float64, bool) { | ||||
| 	// Is there a better way to do this with reflect? | ||||
| 	if d, ok := data.([]interface{}); ok { | ||||
| 		result := make([]float64, len(d)) | ||||
| 		for i, el := range d { | ||||
| 			item, ok := el.(float64) | ||||
| 			if !ok { | ||||
| 				return nil, false | ||||
| 			} | ||||
| 			result[i] = item | ||||
| 		} | ||||
| 		return result, true | ||||
| 	} | ||||
| 	return nil, false | ||||
| } | ||||
|  | ||||
| // ToArrayStr converts an empty interface type to a slice of strings. | ||||
| // If any element in the array cannot be converted, then nil is returned | ||||
| // along with a second value of false.  If the input data could be entirely | ||||
| // converted, then the converted data, along with a second value of true, | ||||
| // will be returned. | ||||
| func toArrayStr(data interface{}) ([]string, bool) { | ||||
| 	// Is there a better way to do this with reflect? | ||||
| 	if d, ok := data.([]interface{}); ok { | ||||
| 		result := make([]string, len(d)) | ||||
| 		for i, el := range d { | ||||
| 			item, ok := el.(string) | ||||
| 			if !ok { | ||||
| 				return nil, false | ||||
| 			} | ||||
| 			result[i] = item | ||||
| 		} | ||||
| 		return result, true | ||||
| 	} | ||||
| 	return nil, false | ||||
| } | ||||
|  | ||||
| func isSliceType(v interface{}) bool { | ||||
| 	if v == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return reflect.TypeOf(v).Kind() == reflect.Slice | ||||
| } | ||||
							
								
								
									
										73
									
								
								vendor/github.com/jmespath/go-jmespath/util_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								vendor/github.com/jmespath/go-jmespath/util_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| package jmespath | ||||
|  | ||||
| import ( | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestSlicePositiveStep(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	input := make([]interface{}, 5) | ||||
| 	input[0] = 0 | ||||
| 	input[1] = 1 | ||||
| 	input[2] = 2 | ||||
| 	input[3] = 3 | ||||
| 	input[4] = 4 | ||||
| 	result, err := slice(input, []sliceParam{{0, true}, {3, true}, {1, true}}) | ||||
| 	assert.Nil(err) | ||||
| 	assert.Equal(input[:3], result) | ||||
| } | ||||
|  | ||||
| func TestIsFalseJSONTypes(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	assert.True(isFalse(false)) | ||||
| 	assert.True(isFalse("")) | ||||
| 	var empty []interface{} | ||||
| 	assert.True(isFalse(empty)) | ||||
| 	m := make(map[string]interface{}) | ||||
| 	assert.True(isFalse(m)) | ||||
| 	assert.True(isFalse(nil)) | ||||
|  | ||||
| } | ||||
|  | ||||
| func TestIsFalseWithUserDefinedStructs(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	type nilStructType struct { | ||||
| 		SliceOfPointers []*string | ||||
| 	} | ||||
| 	nilStruct := nilStructType{SliceOfPointers: nil} | ||||
| 	assert.True(isFalse(nilStruct.SliceOfPointers)) | ||||
|  | ||||
| 	// A user defined struct will never be false though, | ||||
| 	// even if it's fields are the zero type. | ||||
| 	assert.False(isFalse(nilStruct)) | ||||
| } | ||||
|  | ||||
| func TestIsFalseWithNilInterface(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	var a *int = nil | ||||
| 	var nilInterface interface{} | ||||
| 	nilInterface = a | ||||
| 	assert.True(isFalse(nilInterface)) | ||||
| } | ||||
|  | ||||
| func TestIsFalseWithMapOfUserStructs(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	type foo struct { | ||||
| 		Bar string | ||||
| 		Baz string | ||||
| 	} | ||||
| 	m := make(map[int]foo) | ||||
| 	assert.True(isFalse(m)) | ||||
| } | ||||
|  | ||||
| func TestObjsEqual(t *testing.T) { | ||||
| 	assert := assert.New(t) | ||||
| 	assert.True(objsEqual("foo", "foo")) | ||||
| 	assert.True(objsEqual(20, 20)) | ||||
| 	assert.True(objsEqual([]int{1, 2, 3}, []int{1, 2, 3})) | ||||
| 	assert.True(objsEqual(nil, nil)) | ||||
| 	assert.True(!objsEqual(nil, "foo")) | ||||
| 	assert.True(objsEqual([]int{}, []int{})) | ||||
| 	assert.True(!objsEqual([]int{}, nil)) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user