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
12 // PrimitiveWalker implementations are able to handle primitive values
13 // within complex structures. Primitive values are numbers, strings,
14 // booleans, funcs, chans.
16 // These primitive values are often members of more complex
17 // structures (slices, maps, etc.) that are walkable by other interfaces.
18 type PrimitiveWalker interface {
19 Primitive(reflect.Value) error
22 // InterfaceWalker implementations are able to handle interface values as they
23 // are encountered during the walk.
24 type InterfaceWalker interface {
25 Interface(reflect.Value) error
28 // MapWalker implementations are able to handle individual elements
29 // found within a map structure.
30 type MapWalker interface {
31 Map(m reflect.Value) error
32 MapElem(m, k, v reflect.Value) error
35 // SliceWalker implementations are able to handle slice elements found
36 // within complex structures.
37 type SliceWalker interface {
38 Slice(reflect.Value) error
39 SliceElem(int, reflect.Value) error
42 // ArrayWalker implementations are able to handle array elements found
43 // within complex structures.
44 type ArrayWalker interface {
45 Array(reflect.Value) error
46 ArrayElem(int, reflect.Value) error
49 // StructWalker is an interface that has methods that are called for
50 // structs when a Walk is done.
51 type StructWalker interface {
52 Struct(reflect.Value) error
53 StructField(reflect.StructField, reflect.Value) error
56 // EnterExitWalker implementations are notified before and after
57 // they walk deeper into complex structures (into struct fields,
58 // into slice elements, etc.)
59 type EnterExitWalker interface {
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.
67 type PointerWalker interface {
68 PointerEnter(bool) error
69 PointerExit(bool) error
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:
75 // - Struct: skips all fields from being walked
76 // - StructField: skips walking the struct value
78 var SkipEntry = errors.New("skip this entry")
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.
84 func Walk(data, walker interface{}) (err error) {
85 v := reflect.ValueOf(data)
86 ew, ok := walker.(EnterExitWalker)
88 err = ew.Enter(WalkLoc)
96 err = ew.Exit(WalkLoc)
102 func 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.
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.
110 // Check whether the value is then a pointer. If so, then set pointer
111 // to true to notify the user.
113 // If we still have a pointer or an interface after the indirections, then
114 // we unwrap another level
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.
122 if pointerV.Kind() == reflect.Interface {
123 if iw, ok := w.(InterfaceWalker); ok {
124 if err = iw.Interface(pointerV); err != nil {
129 pointerV = pointerV.Elem()
132 if pointerV.Kind() == reflect.Ptr {
134 v = reflect.Indirect(pointerV)
136 if pw, ok := w.(PointerWalker); ok {
137 if err = pw.PointerEnter(pointer); err != nil {
141 defer func(pointer bool) {
146 err = pw.PointerExit(pointer)
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:
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
167 if v.Kind() == reflect.Interface {
172 if k >= reflect.Int && k <= reflect.Complex128 {
178 case reflect.Bool, reflect.Chan, reflect.Func, reflect.Int, reflect.String, reflect.Invalid:
179 err = walkPrimitive(originalV, w)
185 err = walkSlice(v, w)
188 err = walkStruct(v, w)
191 err = walkArray(v, w)
194 panic("unsupported type: " + k.String())
198 func walkMap(v reflect.Value, w interface{}) error {
199 ew, ewok := w.(EnterExitWalker)
204 if mw, ok := w.(MapWalker); ok {
205 if err := mw.Map(v); err != nil {
210 for _, k := range v.MapKeys() {
213 if mw, ok := w.(MapWalker); ok {
214 if err := mw.MapElem(v, k, kv); err != nil {
219 ew, ok := w.(EnterExitWalker)
224 if err := walk(k, w); err != nil {
233 if err := walk(kv, w); err != nil {
249 func walkPrimitive(v reflect.Value, w interface{}) error {
250 if pw, ok := w.(PrimitiveWalker); ok {
251 return pw.Primitive(v)
257 func walkSlice(v reflect.Value, w interface{}) (err error) {
258 ew, ok := w.(EnterExitWalker)
263 if sw, ok := w.(SliceWalker); ok {
264 if err := sw.Slice(v); err != nil {
269 for i := 0; i < v.Len(); i++ {
272 if sw, ok := w.(SliceWalker); ok {
273 if err := sw.SliceElem(i, elem); err != nil {
278 ew, ok := w.(EnterExitWalker)
283 if err := walk(elem, w); err != nil {
292 ew, ok = w.(EnterExitWalker)
300 func walkArray(v reflect.Value, w interface{}) (err error) {
301 ew, ok := w.(EnterExitWalker)
306 if aw, ok := w.(ArrayWalker); ok {
307 if err := aw.Array(v); err != nil {
312 for i := 0; i < v.Len(); i++ {
315 if aw, ok := w.(ArrayWalker); ok {
316 if err := aw.ArrayElem(i, elem); err != nil {
321 ew, ok := w.(EnterExitWalker)
326 if err := walk(elem, w); err != nil {
335 ew, ok = w.(EnterExitWalker)
343 func walkStruct(v reflect.Value, w interface{}) (err error) {
344 ew, ewok := w.(EnterExitWalker)
350 if sw, ok := w.(StructWalker); ok {
352 if err == SkipEntry {
363 for i := 0; i < vt.NumField(); i++ {
365 f := v.FieldByIndex([]int{i})
367 if sw, ok := w.(StructWalker); ok {
368 err = sw.StructField(sf, f)
370 // SkipEntry just pretends this field doesn't even exist
371 if err == SkipEntry {
380 ew, ok := w.(EnterExitWalker)
382 ew.Enter(StructField)