]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/mitchellh/reflectwalk/reflectwalk.go
Merge branch 'master' of /home/ubuntu/terraform-vendor
[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
42// StructWalker is an interface that has methods that are called for
43// structs when a Walk is done.
44type StructWalker interface {
45 Struct(reflect.Value) error
46 StructField(reflect.StructField, reflect.Value) error
47}
48
49// EnterExitWalker implementations are notified before and after
50// they walk deeper into complex structures (into struct fields,
51// into slice elements, etc.)
52type EnterExitWalker interface {
53 Enter(Location) error
54 Exit(Location) error
55}
56
57// PointerWalker implementations are notified when the value they're
58// walking is a pointer or not. Pointer is called for _every_ value whether
59// it is a pointer or not.
60type PointerWalker interface {
61 PointerEnter(bool) error
62 PointerExit(bool) error
63}
64
65// SkipEntry can be returned from walk functions to skip walking
66// the value of this field. This is only valid in the following functions:
67//
68// - StructField: skips walking the struct value
69//
70var SkipEntry = errors.New("skip this entry")
71
72// Walk takes an arbitrary value and an interface and traverses the
73// value, calling callbacks on the interface if they are supported.
74// The interface should implement one or more of the walker interfaces
75// in this package, such as PrimitiveWalker, StructWalker, etc.
76func Walk(data, walker interface{}) (err error) {
77 v := reflect.ValueOf(data)
78 ew, ok := walker.(EnterExitWalker)
79 if ok {
80 err = ew.Enter(WalkLoc)
81 }
82
83 if err == nil {
84 err = walk(v, walker)
85 }
86
87 if ok && err == nil {
88 err = ew.Exit(WalkLoc)
89 }
90
91 return
92}
93
94func walk(v reflect.Value, w interface{}) (err error) {
95 // Determine if we're receiving a pointer and if so notify the walker.
96 // The logic here is convoluted but very important (tests will fail if
97 // almost any part is changed). I will try to explain here.
98 //
99 // First, we check if the value is an interface, if so, we really need
100 // to check the interface's VALUE to see whether it is a pointer.
101 //
102 // Check whether the value is then a pointer. If so, then set pointer
103 // to true to notify the user.
104 //
105 // If we still have a pointer or an interface after the indirections, then
106 // we unwrap another level
107 //
108 // At this time, we also set "v" to be the dereferenced value. This is
109 // because once we've unwrapped the pointer we want to use that value.
110 pointer := false
111 pointerV := v
112
113 for {
114 if pointerV.Kind() == reflect.Interface {
115 if iw, ok := w.(InterfaceWalker); ok {
116 if err = iw.Interface(pointerV); err != nil {
117 return
118 }
119 }
120
121 pointerV = pointerV.Elem()
122 }
123
124 if pointerV.Kind() == reflect.Ptr {
125 pointer = true
126 v = reflect.Indirect(pointerV)
127 }
128 if pw, ok := w.(PointerWalker); ok {
129 if err = pw.PointerEnter(pointer); err != nil {
130 return
131 }
132
133 defer func(pointer bool) {
134 if err != nil {
135 return
136 }
137
138 err = pw.PointerExit(pointer)
139 }(pointer)
140 }
141
142 if pointer {
143 pointerV = v
144 }
145 pointer = false
146
147 // If we still have a pointer or interface we have to indirect another level.
148 switch pointerV.Kind() {
149 case reflect.Ptr, reflect.Interface:
150 continue
151 }
152 break
153 }
154
155 // We preserve the original value here because if it is an interface
156 // type, we want to pass that directly into the walkPrimitive, so that
157 // we can set it.
158 originalV := v
159 if v.Kind() == reflect.Interface {
160 v = v.Elem()
161 }
162
163 k := v.Kind()
164 if k >= reflect.Int && k <= reflect.Complex128 {
165 k = reflect.Int
166 }
167
168 switch k {
169 // Primitives
170 case reflect.Bool, reflect.Chan, reflect.Func, reflect.Int, reflect.String, reflect.Invalid:
171 err = walkPrimitive(originalV, w)
172 return
173 case reflect.Map:
174 err = walkMap(v, w)
175 return
176 case reflect.Slice:
177 err = walkSlice(v, w)
178 return
179 case reflect.Struct:
180 err = walkStruct(v, w)
181 return
182 default:
183 panic("unsupported type: " + k.String())
184 }
185}
186
187func walkMap(v reflect.Value, w interface{}) error {
188 ew, ewok := w.(EnterExitWalker)
189 if ewok {
190 ew.Enter(Map)
191 }
192
193 if mw, ok := w.(MapWalker); ok {
194 if err := mw.Map(v); err != nil {
195 return err
196 }
197 }
198
199 for _, k := range v.MapKeys() {
200 kv := v.MapIndex(k)
201
202 if mw, ok := w.(MapWalker); ok {
203 if err := mw.MapElem(v, k, kv); err != nil {
204 return err
205 }
206 }
207
208 ew, ok := w.(EnterExitWalker)
209 if ok {
210 ew.Enter(MapKey)
211 }
212
213 if err := walk(k, w); err != nil {
214 return err
215 }
216
217 if ok {
218 ew.Exit(MapKey)
219 ew.Enter(MapValue)
220 }
221
222 if err := walk(kv, w); err != nil {
223 return err
224 }
225
226 if ok {
227 ew.Exit(MapValue)
228 }
229 }
230
231 if ewok {
232 ew.Exit(Map)
233 }
234
235 return nil
236}
237
238func walkPrimitive(v reflect.Value, w interface{}) error {
239 if pw, ok := w.(PrimitiveWalker); ok {
240 return pw.Primitive(v)
241 }
242
243 return nil
244}
245
246func walkSlice(v reflect.Value, w interface{}) (err error) {
247 ew, ok := w.(EnterExitWalker)
248 if ok {
249 ew.Enter(Slice)
250 }
251
252 if sw, ok := w.(SliceWalker); ok {
253 if err := sw.Slice(v); err != nil {
254 return err
255 }
256 }
257
258 for i := 0; i < v.Len(); i++ {
259 elem := v.Index(i)
260
261 if sw, ok := w.(SliceWalker); ok {
262 if err := sw.SliceElem(i, elem); err != nil {
263 return err
264 }
265 }
266
267 ew, ok := w.(EnterExitWalker)
268 if ok {
269 ew.Enter(SliceElem)
270 }
271
272 if err := walk(elem, w); err != nil {
273 return err
274 }
275
276 if ok {
277 ew.Exit(SliceElem)
278 }
279 }
280
281 ew, ok = w.(EnterExitWalker)
282 if ok {
283 ew.Exit(Slice)
284 }
285
286 return nil
287}
288
289func walkStruct(v reflect.Value, w interface{}) (err error) {
290 ew, ewok := w.(EnterExitWalker)
291 if ewok {
292 ew.Enter(Struct)
293 }
294
295 if sw, ok := w.(StructWalker); ok {
296 if err = sw.Struct(v); err != nil {
297 return
298 }
299 }
300
301 vt := v.Type()
302 for i := 0; i < vt.NumField(); i++ {
303 sf := vt.Field(i)
304 f := v.FieldByIndex([]int{i})
305
306 if sw, ok := w.(StructWalker); ok {
307 err = sw.StructField(sf, f)
308
309 // SkipEntry just pretends this field doesn't even exist
310 if err == SkipEntry {
311 continue
312 }
313
314 if err != nil {
315 return
316 }
317 }
318
319 ew, ok := w.(EnterExitWalker)
320 if ok {
321 ew.Enter(StructField)
322 }
323
324 err = walk(f, w)
325 if err != nil {
326 return
327 }
328
329 if ok {
330 ew.Exit(StructField)
331 }
332 }
333
334 if ewok {
335 ew.Exit(Struct)
336 }
337
338 return nil
339}