]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/mitchellh/reflectwalk/reflectwalk.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / mitchellh / reflectwalk / reflectwalk.go
CommitLineData
bae9f6d2
JC
1// reflectwalk is a package that allows you to "walk" complex structures
2// similar to how you may "walk" a filesystem: visiting every element one
3// by one and calling callback functions allowing you to handle and manipulate
4// those elements.
5package reflectwalk
6
7import (
8 "errors"
9 "reflect"
10)
11
12// PrimitiveWalker implementations are able to handle primitive values
13// within complex structures. Primitive values are numbers, strings,
14// booleans, funcs, chans.
15//
16// These primitive values are often members of more complex
17// structures (slices, maps, etc.) that are walkable by other interfaces.
18type PrimitiveWalker interface {
19 Primitive(reflect.Value) error
20}
21
22// InterfaceWalker implementations are able to handle interface values as they
23// are encountered during the walk.
24type InterfaceWalker interface {
25 Interface(reflect.Value) error
26}
27
28// MapWalker implementations are able to handle individual elements
29// found within a map structure.
30type MapWalker interface {
31 Map(m reflect.Value) error
32 MapElem(m, k, v reflect.Value) error
33}
34
35// SliceWalker implementations are able to handle slice elements found
36// within complex structures.
37type SliceWalker interface {
38 Slice(reflect.Value) error
39 SliceElem(int, reflect.Value) error
40}
41
15c0b25d
AP
42// ArrayWalker implementations are able to handle array elements found
43// within complex structures.
44type ArrayWalker interface {
45 Array(reflect.Value) error
46 ArrayElem(int, reflect.Value) error
47}
48
bae9f6d2
JC
49// StructWalker is an interface that has methods that are called for
50// structs when a Walk is done.
51type StructWalker interface {
52 Struct(reflect.Value) error
53 StructField(reflect.StructField, reflect.Value) error
54}
55
56// EnterExitWalker implementations are notified before and after
57// they walk deeper into complex structures (into struct fields,
58// into slice elements, etc.)
59type EnterExitWalker interface {
60 Enter(Location) error
61 Exit(Location) error
62}
63
64// PointerWalker implementations are notified when the value they're
65// walking is a pointer or not. Pointer is called for _every_ value whether
66// it is a pointer or not.
67type PointerWalker interface {
68 PointerEnter(bool) error
69 PointerExit(bool) error
70}
71
72// SkipEntry can be returned from walk functions to skip walking
73// the value of this field. This is only valid in the following functions:
74//
15c0b25d 75// - Struct: skips all fields from being walked
bae9f6d2
JC
76// - StructField: skips walking the struct value
77//
78var SkipEntry = errors.New("skip this entry")
79
80// Walk takes an arbitrary value and an interface and traverses the
81// value, calling callbacks on the interface if they are supported.
82// The interface should implement one or more of the walker interfaces
83// in this package, such as PrimitiveWalker, StructWalker, etc.
84func Walk(data, walker interface{}) (err error) {
85 v := reflect.ValueOf(data)
86 ew, ok := walker.(EnterExitWalker)
87 if ok {
88 err = ew.Enter(WalkLoc)
89 }
90
91 if err == nil {
92 err = walk(v, walker)
93 }
94
95 if ok && err == nil {
96 err = ew.Exit(WalkLoc)
97 }
98
99 return
100}
101
102func walk(v reflect.Value, w interface{}) (err error) {
103 // Determine if we're receiving a pointer and if so notify the walker.
104 // The logic here is convoluted but very important (tests will fail if
105 // almost any part is changed). I will try to explain here.
106 //
107 // First, we check if the value is an interface, if so, we really need
108 // to check the interface's VALUE to see whether it is a pointer.
109 //
110 // Check whether the value is then a pointer. If so, then set pointer
111 // to true to notify the user.
112 //
113 // If we still have a pointer or an interface after the indirections, then
114 // we unwrap another level
115 //
116 // At this time, we also set "v" to be the dereferenced value. This is
117 // because once we've unwrapped the pointer we want to use that value.
118 pointer := false
119 pointerV := v
120
121 for {
122 if pointerV.Kind() == reflect.Interface {
123 if iw, ok := w.(InterfaceWalker); ok {
124 if err = iw.Interface(pointerV); err != nil {
125 return
126 }
127 }
128
129 pointerV = pointerV.Elem()
130 }
131
132 if pointerV.Kind() == reflect.Ptr {
133 pointer = true
134 v = reflect.Indirect(pointerV)
135 }
136 if pw, ok := w.(PointerWalker); ok {
137 if err = pw.PointerEnter(pointer); err != nil {
138 return
139 }
140
141 defer func(pointer bool) {
142 if err != nil {
143 return
144 }
145
146 err = pw.PointerExit(pointer)
147 }(pointer)
148 }
149
150 if pointer {
151 pointerV = v
152 }
153 pointer = false
154
155 // If we still have a pointer or interface we have to indirect another level.
156 switch pointerV.Kind() {
157 case reflect.Ptr, reflect.Interface:
158 continue
159 }
160 break
161 }
162
163 // We preserve the original value here because if it is an interface
164 // type, we want to pass that directly into the walkPrimitive, so that
165 // we can set it.
166 originalV := v
167 if v.Kind() == reflect.Interface {
168 v = v.Elem()
169 }
170
171 k := v.Kind()
172 if k >= reflect.Int && k <= reflect.Complex128 {
173 k = reflect.Int
174 }
175
176 switch k {
177 // Primitives
178 case reflect.Bool, reflect.Chan, reflect.Func, reflect.Int, reflect.String, reflect.Invalid:
179 err = walkPrimitive(originalV, w)
180 return
181 case reflect.Map:
182 err = walkMap(v, w)
183 return
184 case reflect.Slice:
185 err = walkSlice(v, w)
186 return
187 case reflect.Struct:
188 err = walkStruct(v, w)
189 return
15c0b25d
AP
190 case reflect.Array:
191 err = walkArray(v, w)
192 return
bae9f6d2
JC
193 default:
194 panic("unsupported type: " + k.String())
195 }
196}
197
198func walkMap(v reflect.Value, w interface{}) error {
199 ew, ewok := w.(EnterExitWalker)
200 if ewok {
201 ew.Enter(Map)
202 }
203
204 if mw, ok := w.(MapWalker); ok {
205 if err := mw.Map(v); err != nil {
206 return err
207 }
208 }
209
210 for _, k := range v.MapKeys() {
211 kv := v.MapIndex(k)
212
213 if mw, ok := w.(MapWalker); ok {
214 if err := mw.MapElem(v, k, kv); err != nil {
215 return err
216 }
217 }
218
219 ew, ok := w.(EnterExitWalker)
220 if ok {
221 ew.Enter(MapKey)
222 }
223
224 if err := walk(k, w); err != nil {
225 return err
226 }
227
228 if ok {
229 ew.Exit(MapKey)
230 ew.Enter(MapValue)
231 }
232
233 if err := walk(kv, w); err != nil {
234 return err
235 }
236
237 if ok {
238 ew.Exit(MapValue)
239 }
240 }
241
242 if ewok {
243 ew.Exit(Map)
244 }
245
246 return nil
247}
248
249func walkPrimitive(v reflect.Value, w interface{}) error {
250 if pw, ok := w.(PrimitiveWalker); ok {
251 return pw.Primitive(v)
252 }
253
254 return nil
255}
256
257func walkSlice(v reflect.Value, w interface{}) (err error) {
258 ew, ok := w.(EnterExitWalker)
259 if ok {
260 ew.Enter(Slice)
261 }
262
263 if sw, ok := w.(SliceWalker); ok {
264 if err := sw.Slice(v); err != nil {
265 return err
266 }
267 }
268
269 for i := 0; i < v.Len(); i++ {
270 elem := v.Index(i)
271
272 if sw, ok := w.(SliceWalker); ok {
273 if err := sw.SliceElem(i, elem); err != nil {
274 return err
275 }
276 }
277
278 ew, ok := w.(EnterExitWalker)
279 if ok {
280 ew.Enter(SliceElem)
281 }
282
283 if err := walk(elem, w); err != nil {
284 return err
285 }
286
287 if ok {
288 ew.Exit(SliceElem)
289 }
290 }
291
292 ew, ok = w.(EnterExitWalker)
293 if ok {
294 ew.Exit(Slice)
295 }
296
297 return nil
298}
299
15c0b25d
AP
300func walkArray(v reflect.Value, w interface{}) (err error) {
301 ew, ok := w.(EnterExitWalker)
302 if ok {
303 ew.Enter(Array)
304 }
305
306 if aw, ok := w.(ArrayWalker); ok {
307 if err := aw.Array(v); err != nil {
308 return err
309 }
310 }
311
312 for i := 0; i < v.Len(); i++ {
313 elem := v.Index(i)
314
315 if aw, ok := w.(ArrayWalker); ok {
316 if err := aw.ArrayElem(i, elem); err != nil {
317 return err
318 }
319 }
320
321 ew, ok := w.(EnterExitWalker)
322 if ok {
323 ew.Enter(ArrayElem)
324 }
325
326 if err := walk(elem, w); err != nil {
327 return err
328 }
329
330 if ok {
331 ew.Exit(ArrayElem)
332 }
333 }
334
335 ew, ok = w.(EnterExitWalker)
336 if ok {
337 ew.Exit(Array)
338 }
339
340 return nil
341}
342
bae9f6d2
JC
343func walkStruct(v reflect.Value, w interface{}) (err error) {
344 ew, ewok := w.(EnterExitWalker)
345 if ewok {
346 ew.Enter(Struct)
347 }
348
15c0b25d 349 skip := false
bae9f6d2 350 if sw, ok := w.(StructWalker); ok {
15c0b25d
AP
351 err = sw.Struct(v)
352 if err == SkipEntry {
353 skip = true
354 err = nil
355 }
356 if err != nil {
bae9f6d2
JC
357 return
358 }
359 }
360
15c0b25d
AP
361 if !skip {
362 vt := v.Type()
363 for i := 0; i < vt.NumField(); i++ {
364 sf := vt.Field(i)
365 f := v.FieldByIndex([]int{i})
bae9f6d2 366
15c0b25d
AP
367 if sw, ok := w.(StructWalker); ok {
368 err = sw.StructField(sf, f)
bae9f6d2 369
15c0b25d
AP
370 // SkipEntry just pretends this field doesn't even exist
371 if err == SkipEntry {
372 continue
373 }
374
375 if err != nil {
376 return
377 }
378 }
379
380 ew, ok := w.(EnterExitWalker)
381 if ok {
382 ew.Enter(StructField)
bae9f6d2
JC
383 }
384
15c0b25d 385 err = walk(f, w)
bae9f6d2
JC
386 if err != nil {
387 return
388 }
bae9f6d2 389
15c0b25d
AP
390 if ok {
391 ew.Exit(StructField)
392 }
bae9f6d2
JC
393 }
394 }
395
396 if ewok {
397 ew.Exit(Struct)
398 }
399
400 return nil
401}