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 // StructWalker is an interface that has methods that are called for
43 // structs when a Walk is done.
44 type StructWalker interface {
45 Struct(reflect.Value) error
46 StructField(reflect.StructField, reflect.Value) error
49 // EnterExitWalker implementations are notified before and after
50 // they walk deeper into complex structures (into struct fields,
51 // into slice elements, etc.)
52 type EnterExitWalker interface {
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.
60 type PointerWalker interface {
61 PointerEnter(bool) error
62 PointerExit(bool) error
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:
68 // - StructField: skips walking the struct value
70 var SkipEntry = errors.New("skip this entry")
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.
76 func Walk(data, walker interface{}) (err error) {
77 v := reflect.ValueOf(data)
78 ew, ok := walker.(EnterExitWalker)
80 err = ew.Enter(WalkLoc)
88 err = ew.Exit(WalkLoc)
94 func 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.
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.
102 // Check whether the value is then a pointer. If so, then set pointer
103 // to true to notify the user.
105 // If we still have a pointer or an interface after the indirections, then
106 // we unwrap another level
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.
114 if pointerV.Kind() == reflect.Interface {
115 if iw, ok := w.(InterfaceWalker); ok {
116 if err = iw.Interface(pointerV); err != nil {
121 pointerV = pointerV.Elem()
124 if pointerV.Kind() == reflect.Ptr {
126 v = reflect.Indirect(pointerV)
128 if pw, ok := w.(PointerWalker); ok {
129 if err = pw.PointerEnter(pointer); err != nil {
133 defer func(pointer bool) {
138 err = pw.PointerExit(pointer)
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:
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
159 if v.Kind() == reflect.Interface {
164 if k >= reflect.Int && k <= reflect.Complex128 {
170 case reflect.Bool, reflect.Chan, reflect.Func, reflect.Int, reflect.String, reflect.Invalid:
171 err = walkPrimitive(originalV, w)
177 err = walkSlice(v, w)
180 err = walkStruct(v, w)
183 panic("unsupported type: " + k.String())
187 func walkMap(v reflect.Value, w interface{}) error {
188 ew, ewok := w.(EnterExitWalker)
193 if mw, ok := w.(MapWalker); ok {
194 if err := mw.Map(v); err != nil {
199 for _, k := range v.MapKeys() {
202 if mw, ok := w.(MapWalker); ok {
203 if err := mw.MapElem(v, k, kv); err != nil {
208 ew, ok := w.(EnterExitWalker)
213 if err := walk(k, w); err != nil {
222 if err := walk(kv, w); err != nil {
238 func walkPrimitive(v reflect.Value, w interface{}) error {
239 if pw, ok := w.(PrimitiveWalker); ok {
240 return pw.Primitive(v)
246 func walkSlice(v reflect.Value, w interface{}) (err error) {
247 ew, ok := w.(EnterExitWalker)
252 if sw, ok := w.(SliceWalker); ok {
253 if err := sw.Slice(v); err != nil {
258 for i := 0; i < v.Len(); i++ {
261 if sw, ok := w.(SliceWalker); ok {
262 if err := sw.SliceElem(i, elem); err != nil {
267 ew, ok := w.(EnterExitWalker)
272 if err := walk(elem, w); err != nil {
281 ew, ok = w.(EnterExitWalker)
289 func walkStruct(v reflect.Value, w interface{}) (err error) {
290 ew, ewok := w.(EnterExitWalker)
295 if sw, ok := w.(StructWalker); ok {
296 if err = sw.Struct(v); err != nil {
302 for i := 0; i < vt.NumField(); i++ {
304 f := v.FieldByIndex([]int{i})
306 if sw, ok := w.(StructWalker); ok {
307 err = sw.StructField(sf, f)
309 // SkipEntry just pretends this field doesn't even exist
310 if err == SkipEntry {
319 ew, ok := w.(EnterExitWalker)
321 ew.Enter(StructField)