a685e3fc98
Vndr has a simpler configuration and allows pointing to forked packages. Additionally other docker projects are now using vndr making vendoring in distribution more consistent. Updates letsencrypt to use fork. No longer uses sub-vendored packages. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
419 lines
10 KiB
Go
419 lines
10 KiB
Go
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
|
|
}
|