forked from TrueCloudLab/distribution
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)
185 lines
4.2 KiB
Go
185 lines
4.2 KiB
Go
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
|
|
}
|