]>
Commit | Line | Data |
---|---|---|
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 | } |