]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package awsutil |
2 | ||
3 | import ( | |
4 | "reflect" | |
5 | "regexp" | |
6 | "strconv" | |
7 | "strings" | |
8 | ||
9 | "github.com/jmespath/go-jmespath" | |
10 | ) | |
11 | ||
12 | var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`) | |
13 | ||
14 | // rValuesAtPath returns a slice of values found in value v. The values | |
15 | // in v are explored recursively so all nested values are collected. | |
16 | func rValuesAtPath(v interface{}, path string, createPath, caseSensitive, nilTerm bool) []reflect.Value { | |
17 | pathparts := strings.Split(path, "||") | |
18 | if len(pathparts) > 1 { | |
19 | for _, pathpart := range pathparts { | |
20 | vals := rValuesAtPath(v, pathpart, createPath, caseSensitive, nilTerm) | |
21 | if len(vals) > 0 { | |
22 | return vals | |
23 | } | |
24 | } | |
25 | return nil | |
26 | } | |
27 | ||
28 | values := []reflect.Value{reflect.Indirect(reflect.ValueOf(v))} | |
29 | components := strings.Split(path, ".") | |
30 | for len(values) > 0 && len(components) > 0 { | |
31 | var index *int64 | |
32 | var indexStar bool | |
33 | c := strings.TrimSpace(components[0]) | |
34 | if c == "" { // no actual component, illegal syntax | |
35 | return nil | |
36 | } else if caseSensitive && c != "*" && strings.ToLower(c[0:1]) == c[0:1] { | |
37 | // TODO normalize case for user | |
38 | return nil // don't support unexported fields | |
39 | } | |
40 | ||
41 | // parse this component | |
42 | if m := indexRe.FindStringSubmatch(c); m != nil { | |
43 | c = m[1] | |
44 | if m[2] == "" { | |
45 | index = nil | |
46 | indexStar = true | |
47 | } else { | |
48 | i, _ := strconv.ParseInt(m[2], 10, 32) | |
49 | index = &i | |
50 | indexStar = false | |
51 | } | |
52 | } | |
53 | ||
54 | nextvals := []reflect.Value{} | |
55 | for _, value := range values { | |
56 | // pull component name out of struct member | |
57 | if value.Kind() != reflect.Struct { | |
58 | continue | |
59 | } | |
60 | ||
61 | if c == "*" { // pull all members | |
62 | for i := 0; i < value.NumField(); i++ { | |
63 | if f := reflect.Indirect(value.Field(i)); f.IsValid() { | |
64 | nextvals = append(nextvals, f) | |
65 | } | |
66 | } | |
67 | continue | |
68 | } | |
69 | ||
70 | value = value.FieldByNameFunc(func(name string) bool { | |
71 | if c == name { | |
72 | return true | |
73 | } else if !caseSensitive && strings.ToLower(name) == strings.ToLower(c) { | |
74 | return true | |
75 | } | |
76 | return false | |
77 | }) | |
78 | ||
79 | if nilTerm && value.Kind() == reflect.Ptr && len(components[1:]) == 0 { | |
80 | if !value.IsNil() { | |
81 | value.Set(reflect.Zero(value.Type())) | |
82 | } | |
83 | return []reflect.Value{value} | |
84 | } | |
85 | ||
86 | if createPath && value.Kind() == reflect.Ptr && value.IsNil() { | |
87 | // TODO if the value is the terminus it should not be created | |
88 | // if the value to be set to its position is nil. | |
89 | value.Set(reflect.New(value.Type().Elem())) | |
90 | value = value.Elem() | |
91 | } else { | |
92 | value = reflect.Indirect(value) | |
93 | } | |
94 | ||
95 | if value.Kind() == reflect.Slice || value.Kind() == reflect.Map { | |
96 | if !createPath && value.IsNil() { | |
97 | value = reflect.ValueOf(nil) | |
98 | } | |
99 | } | |
100 | ||
101 | if value.IsValid() { | |
102 | nextvals = append(nextvals, value) | |
103 | } | |
104 | } | |
105 | values = nextvals | |
106 | ||
107 | if indexStar || index != nil { | |
108 | nextvals = []reflect.Value{} | |
109 | for _, valItem := range values { | |
110 | value := reflect.Indirect(valItem) | |
111 | if value.Kind() != reflect.Slice { | |
112 | continue | |
113 | } | |
114 | ||
115 | if indexStar { // grab all indices | |
116 | for i := 0; i < value.Len(); i++ { | |
117 | idx := reflect.Indirect(value.Index(i)) | |
118 | if idx.IsValid() { | |
119 | nextvals = append(nextvals, idx) | |
120 | } | |
121 | } | |
122 | continue | |
123 | } | |
124 | ||
125 | // pull out index | |
126 | i := int(*index) | |
127 | if i >= value.Len() { // check out of bounds | |
128 | if createPath { | |
129 | // TODO resize slice | |
130 | } else { | |
131 | continue | |
132 | } | |
133 | } else if i < 0 { // support negative indexing | |
134 | i = value.Len() + i | |
135 | } | |
136 | value = reflect.Indirect(value.Index(i)) | |
137 | ||
138 | if value.Kind() == reflect.Slice || value.Kind() == reflect.Map { | |
139 | if !createPath && value.IsNil() { | |
140 | value = reflect.ValueOf(nil) | |
141 | } | |
142 | } | |
143 | ||
144 | if value.IsValid() { | |
145 | nextvals = append(nextvals, value) | |
146 | } | |
147 | } | |
148 | values = nextvals | |
149 | } | |
150 | ||
151 | components = components[1:] | |
152 | } | |
153 | return values | |
154 | } | |
155 | ||
156 | // ValuesAtPath returns a list of values at the case insensitive lexical | |
157 | // path inside of a structure. | |
158 | func ValuesAtPath(i interface{}, path string) ([]interface{}, error) { | |
159 | result, err := jmespath.Search(path, i) | |
160 | if err != nil { | |
161 | return nil, err | |
162 | } | |
163 | ||
164 | v := reflect.ValueOf(result) | |
165 | if !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) { | |
166 | return nil, nil | |
167 | } | |
168 | if s, ok := result.([]interface{}); ok { | |
169 | return s, err | |
170 | } | |
171 | if v.Kind() == reflect.Map && v.Len() == 0 { | |
172 | return nil, nil | |
173 | } | |
174 | if v.Kind() == reflect.Slice { | |
175 | out := make([]interface{}, v.Len()) | |
176 | for i := 0; i < v.Len(); i++ { | |
177 | out[i] = v.Index(i).Interface() | |
178 | } | |
179 | return out, nil | |
180 | } | |
181 | ||
182 | return []interface{}{result}, nil | |
183 | } | |
184 | ||
185 | // SetValueAtPath sets a value at the case insensitive lexical path inside | |
186 | // of a structure. | |
187 | func SetValueAtPath(i interface{}, path string, v interface{}) { | |
188 | if rvals := rValuesAtPath(i, path, true, false, v == nil); rvals != nil { | |
189 | for _, rval := range rvals { | |
190 | if rval.Kind() == reflect.Ptr && rval.IsNil() { | |
191 | continue | |
192 | } | |
193 | setValue(rval, v) | |
194 | } | |
195 | } | |
196 | } | |
197 | ||
198 | func setValue(dstVal reflect.Value, src interface{}) { | |
199 | if dstVal.Kind() == reflect.Ptr { | |
200 | dstVal = reflect.Indirect(dstVal) | |
201 | } | |
202 | srcVal := reflect.ValueOf(src) | |
203 | ||
204 | if !srcVal.IsValid() { // src is literal nil | |
205 | if dstVal.CanAddr() { | |
206 | // Convert to pointer so that pointer's value can be nil'ed | |
207 | // dstVal = dstVal.Addr() | |
208 | } | |
209 | dstVal.Set(reflect.Zero(dstVal.Type())) | |
210 | ||
211 | } else if srcVal.Kind() == reflect.Ptr { | |
212 | if srcVal.IsNil() { | |
213 | srcVal = reflect.Zero(dstVal.Type()) | |
214 | } else { | |
215 | srcVal = reflect.ValueOf(src).Elem() | |
216 | } | |
217 | dstVal.Set(srcVal) | |
218 | } else { | |
219 | dstVal.Set(srcVal) | |
220 | } | |
221 | ||
222 | } |