| 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 |
| } |