util.go (4334B)
1 package jmespath 2 3 import ( 4 "errors" 5 "reflect" 6 ) 7 8 // IsFalse determines if an object is false based on the JMESPath spec. 9 // JMESPath defines false values to be any of: 10 // - An empty string array, or hash. 11 // - The boolean value false. 12 // - nil 13 func isFalse(value interface{}) bool { 14 switch v := value.(type) { 15 case bool: 16 return !v 17 case []interface{}: 18 return len(v) == 0 19 case map[string]interface{}: 20 return len(v) == 0 21 case string: 22 return len(v) == 0 23 case nil: 24 return true 25 } 26 // Try the reflection cases before returning false. 27 rv := reflect.ValueOf(value) 28 switch rv.Kind() { 29 case reflect.Struct: 30 // A struct type will never be false, even if 31 // all of its values are the zero type. 32 return false 33 case reflect.Slice, reflect.Map: 34 return rv.Len() == 0 35 case reflect.Ptr: 36 if rv.IsNil() { 37 return true 38 } 39 // If it's a pointer type, we'll try to deref the pointer 40 // and evaluate the pointer value for isFalse. 41 element := rv.Elem() 42 return isFalse(element.Interface()) 43 } 44 return false 45 } 46 47 // ObjsEqual is a generic object equality check. 48 // It will take two arbitrary objects and recursively determine 49 // if they are equal. 50 func objsEqual(left interface{}, right interface{}) bool { 51 return reflect.DeepEqual(left, right) 52 } 53 54 // SliceParam refers to a single part of a slice. 55 // A slice consists of a start, a stop, and a step, similar to 56 // python slices. 57 type sliceParam struct { 58 N int 59 Specified bool 60 } 61 62 // Slice supports [start:stop:step] style slicing that's supported in JMESPath. 63 func slice(slice []interface{}, parts []sliceParam) ([]interface{}, error) { 64 computed, err := computeSliceParams(len(slice), parts) 65 if err != nil { 66 return nil, err 67 } 68 start, stop, step := computed[0], computed[1], computed[2] 69 result := []interface{}{} 70 if step > 0 { 71 for i := start; i < stop; i += step { 72 result = append(result, slice[i]) 73 } 74 } else { 75 for i := start; i > stop; i += step { 76 result = append(result, slice[i]) 77 } 78 } 79 return result, nil 80 } 81 82 func computeSliceParams(length int, parts []sliceParam) ([]int, error) { 83 var start, stop, step int 84 if !parts[2].Specified { 85 step = 1 86 } else if parts[2].N == 0 { 87 return nil, errors.New("Invalid slice, step cannot be 0") 88 } else { 89 step = parts[2].N 90 } 91 var stepValueNegative bool 92 if step < 0 { 93 stepValueNegative = true 94 } else { 95 stepValueNegative = false 96 } 97 98 if !parts[0].Specified { 99 if stepValueNegative { 100 start = length - 1 101 } else { 102 start = 0 103 } 104 } else { 105 start = capSlice(length, parts[0].N, step) 106 } 107 108 if !parts[1].Specified { 109 if stepValueNegative { 110 stop = -1 111 } else { 112 stop = length 113 } 114 } else { 115 stop = capSlice(length, parts[1].N, step) 116 } 117 return []int{start, stop, step}, nil 118 } 119 120 func capSlice(length int, actual int, step int) int { 121 if actual < 0 { 122 actual += length 123 if actual < 0 { 124 if step < 0 { 125 actual = -1 126 } else { 127 actual = 0 128 } 129 } 130 } else if actual >= length { 131 if step < 0 { 132 actual = length - 1 133 } else { 134 actual = length 135 } 136 } 137 return actual 138 } 139 140 // ToArrayNum converts an empty interface type to a slice of float64. 141 // If any element in the array cannot be converted, then nil is returned 142 // along with a second value of false. 143 func toArrayNum(data interface{}) ([]float64, bool) { 144 // Is there a better way to do this with reflect? 145 if d, ok := data.([]interface{}); ok { 146 result := make([]float64, len(d)) 147 for i, el := range d { 148 item, ok := el.(float64) 149 if !ok { 150 return nil, false 151 } 152 result[i] = item 153 } 154 return result, true 155 } 156 return nil, false 157 } 158 159 // ToArrayStr converts an empty interface type to a slice of strings. 160 // If any element in the array cannot be converted, then nil is returned 161 // along with a second value of false. If the input data could be entirely 162 // converted, then the converted data, along with a second value of true, 163 // will be returned. 164 func toArrayStr(data interface{}) ([]string, bool) { 165 // Is there a better way to do this with reflect? 166 if d, ok := data.([]interface{}); ok { 167 result := make([]string, len(d)) 168 for i, el := range d { 169 item, ok := el.(string) 170 if !ok { 171 return nil, false 172 } 173 result[i] = item 174 } 175 return result, true 176 } 177 return nil, false 178 } 179 180 func isSliceType(v interface{}) bool { 181 if v == nil { 182 return false 183 } 184 return reflect.TypeOf(v).Kind() == reflect.Slice 185 }