...

Source file src/github.com/mitchellh/reflectwalk/reflectwalk.go

Documentation: github.com/mitchellh/reflectwalk

     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.
     5  package reflectwalk
     6  
     7  import (
     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.
    18  type 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.
    24  type InterfaceWalker interface {
    25  	Interface(reflect.Value) error
    26  }
    27  
    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
    33  }
    34  
    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
    40  }
    41  
    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
    47  }
    48  
    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
    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.)
    59  type 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.
    67  type PointerWalker interface {
    68  	PointerEnter(bool) error
    69  	PointerExit(bool) error
    70  }
    71  
    72  // PointerValueWalker implementations are notified with the value of
    73  // a particular pointer when a pointer is walked. Pointer is called
    74  // right before PointerEnter.
    75  type PointerValueWalker interface {
    76  	Pointer(reflect.Value) error
    77  }
    78  
    79  // SkipEntry can be returned from walk functions to skip walking
    80  // the value of this field. This is only valid in the following functions:
    81  //
    82  //   - Struct: skips all fields from being walked
    83  //   - StructField: skips walking the struct value
    84  //
    85  var SkipEntry = errors.New("skip this entry")
    86  
    87  // Walk takes an arbitrary value and an interface and traverses the
    88  // value, calling callbacks on the interface if they are supported.
    89  // The interface should implement one or more of the walker interfaces
    90  // in this package, such as PrimitiveWalker, StructWalker, etc.
    91  func Walk(data, walker interface{}) (err error) {
    92  	v := reflect.ValueOf(data)
    93  	ew, ok := walker.(EnterExitWalker)
    94  	if ok {
    95  		err = ew.Enter(WalkLoc)
    96  	}
    97  
    98  	if err == nil {
    99  		err = walk(v, walker)
   100  	}
   101  
   102  	if ok && err == nil {
   103  		err = ew.Exit(WalkLoc)
   104  	}
   105  
   106  	return
   107  }
   108  
   109  func walk(v reflect.Value, w interface{}) (err error) {
   110  	// Determine if we're receiving a pointer and if so notify the walker.
   111  	// The logic here is convoluted but very important (tests will fail if
   112  	// almost any part is changed). I will try to explain here.
   113  	//
   114  	// First, we check if the value is an interface, if so, we really need
   115  	// to check the interface's VALUE to see whether it is a pointer.
   116  	//
   117  	// Check whether the value is then a pointer. If so, then set pointer
   118  	// to true to notify the user.
   119  	//
   120  	// If we still have a pointer or an interface after the indirections, then
   121  	// we unwrap another level
   122  	//
   123  	// At this time, we also set "v" to be the dereferenced value. This is
   124  	// because once we've unwrapped the pointer we want to use that value.
   125  	pointer := false
   126  	pointerV := v
   127  
   128  	for {
   129  		if pointerV.Kind() == reflect.Interface {
   130  			if iw, ok := w.(InterfaceWalker); ok {
   131  				if err = iw.Interface(pointerV); err != nil {
   132  					return
   133  				}
   134  			}
   135  
   136  			pointerV = pointerV.Elem()
   137  		}
   138  
   139  		if pointerV.Kind() == reflect.Ptr {
   140  			if pw, ok := w.(PointerValueWalker); ok {
   141  				if err = pw.Pointer(pointerV); err != nil {
   142  					if err == SkipEntry {
   143  						// Skip the rest of this entry but clear the error
   144  						return nil
   145  					}
   146  
   147  					return
   148  				}
   149  			}
   150  
   151  			pointer = true
   152  			v = reflect.Indirect(pointerV)
   153  		}
   154  		if pw, ok := w.(PointerWalker); ok {
   155  			if err = pw.PointerEnter(pointer); err != nil {
   156  				return
   157  			}
   158  
   159  			defer func(pointer bool) {
   160  				if err != nil {
   161  					return
   162  				}
   163  
   164  				err = pw.PointerExit(pointer)
   165  			}(pointer)
   166  		}
   167  
   168  		if pointer {
   169  			pointerV = v
   170  		}
   171  		pointer = false
   172  
   173  		// If we still have a pointer or interface we have to indirect another level.
   174  		switch pointerV.Kind() {
   175  		case reflect.Ptr, reflect.Interface:
   176  			continue
   177  		}
   178  		break
   179  	}
   180  
   181  	// We preserve the original value here because if it is an interface
   182  	// type, we want to pass that directly into the walkPrimitive, so that
   183  	// we can set it.
   184  	originalV := v
   185  	if v.Kind() == reflect.Interface {
   186  		v = v.Elem()
   187  	}
   188  
   189  	k := v.Kind()
   190  	if k >= reflect.Int && k <= reflect.Complex128 {
   191  		k = reflect.Int
   192  	}
   193  
   194  	switch k {
   195  	// Primitives
   196  	case reflect.Bool, reflect.Chan, reflect.Func, reflect.Int, reflect.String, reflect.Invalid:
   197  		err = walkPrimitive(originalV, w)
   198  		return
   199  	case reflect.Map:
   200  		err = walkMap(v, w)
   201  		return
   202  	case reflect.Slice:
   203  		err = walkSlice(v, w)
   204  		return
   205  	case reflect.Struct:
   206  		err = walkStruct(v, w)
   207  		return
   208  	case reflect.Array:
   209  		err = walkArray(v, w)
   210  		return
   211  	default:
   212  		panic("unsupported type: " + k.String())
   213  	}
   214  }
   215  
   216  func walkMap(v reflect.Value, w interface{}) error {
   217  	ew, ewok := w.(EnterExitWalker)
   218  	if ewok {
   219  		ew.Enter(Map)
   220  	}
   221  
   222  	if mw, ok := w.(MapWalker); ok {
   223  		if err := mw.Map(v); err != nil {
   224  			return err
   225  		}
   226  	}
   227  
   228  	for _, k := range v.MapKeys() {
   229  		kv := v.MapIndex(k)
   230  
   231  		if mw, ok := w.(MapWalker); ok {
   232  			if err := mw.MapElem(v, k, kv); err != nil {
   233  				return err
   234  			}
   235  		}
   236  
   237  		ew, ok := w.(EnterExitWalker)
   238  		if ok {
   239  			ew.Enter(MapKey)
   240  		}
   241  
   242  		if err := walk(k, w); err != nil {
   243  			return err
   244  		}
   245  
   246  		if ok {
   247  			ew.Exit(MapKey)
   248  			ew.Enter(MapValue)
   249  		}
   250  
   251  		// get the map value again as it may have changed in the MapElem call
   252  		if err := walk(v.MapIndex(k), w); err != nil {
   253  			return err
   254  		}
   255  
   256  		if ok {
   257  			ew.Exit(MapValue)
   258  		}
   259  	}
   260  
   261  	if ewok {
   262  		ew.Exit(Map)
   263  	}
   264  
   265  	return nil
   266  }
   267  
   268  func walkPrimitive(v reflect.Value, w interface{}) error {
   269  	if pw, ok := w.(PrimitiveWalker); ok {
   270  		return pw.Primitive(v)
   271  	}
   272  
   273  	return nil
   274  }
   275  
   276  func walkSlice(v reflect.Value, w interface{}) (err error) {
   277  	ew, ok := w.(EnterExitWalker)
   278  	if ok {
   279  		ew.Enter(Slice)
   280  	}
   281  
   282  	if sw, ok := w.(SliceWalker); ok {
   283  		if err := sw.Slice(v); err != nil {
   284  			return err
   285  		}
   286  	}
   287  
   288  	for i := 0; i < v.Len(); i++ {
   289  		elem := v.Index(i)
   290  
   291  		if sw, ok := w.(SliceWalker); ok {
   292  			if err := sw.SliceElem(i, elem); err != nil {
   293  				return err
   294  			}
   295  		}
   296  
   297  		ew, ok := w.(EnterExitWalker)
   298  		if ok {
   299  			ew.Enter(SliceElem)
   300  		}
   301  
   302  		if err := walk(elem, w); err != nil {
   303  			return err
   304  		}
   305  
   306  		if ok {
   307  			ew.Exit(SliceElem)
   308  		}
   309  	}
   310  
   311  	ew, ok = w.(EnterExitWalker)
   312  	if ok {
   313  		ew.Exit(Slice)
   314  	}
   315  
   316  	return nil
   317  }
   318  
   319  func walkArray(v reflect.Value, w interface{}) (err error) {
   320  	ew, ok := w.(EnterExitWalker)
   321  	if ok {
   322  		ew.Enter(Array)
   323  	}
   324  
   325  	if aw, ok := w.(ArrayWalker); ok {
   326  		if err := aw.Array(v); err != nil {
   327  			return err
   328  		}
   329  	}
   330  
   331  	for i := 0; i < v.Len(); i++ {
   332  		elem := v.Index(i)
   333  
   334  		if aw, ok := w.(ArrayWalker); ok {
   335  			if err := aw.ArrayElem(i, elem); err != nil {
   336  				return err
   337  			}
   338  		}
   339  
   340  		ew, ok := w.(EnterExitWalker)
   341  		if ok {
   342  			ew.Enter(ArrayElem)
   343  		}
   344  
   345  		if err := walk(elem, w); err != nil {
   346  			return err
   347  		}
   348  
   349  		if ok {
   350  			ew.Exit(ArrayElem)
   351  		}
   352  	}
   353  
   354  	ew, ok = w.(EnterExitWalker)
   355  	if ok {
   356  		ew.Exit(Array)
   357  	}
   358  
   359  	return nil
   360  }
   361  
   362  func walkStruct(v reflect.Value, w interface{}) (err error) {
   363  	ew, ewok := w.(EnterExitWalker)
   364  	if ewok {
   365  		ew.Enter(Struct)
   366  	}
   367  
   368  	skip := false
   369  	if sw, ok := w.(StructWalker); ok {
   370  		err = sw.Struct(v)
   371  		if err == SkipEntry {
   372  			skip = true
   373  			err = nil
   374  		}
   375  		if err != nil {
   376  			return
   377  		}
   378  	}
   379  
   380  	if !skip {
   381  		vt := v.Type()
   382  		for i := 0; i < vt.NumField(); i++ {
   383  			sf := vt.Field(i)
   384  			f := v.FieldByIndex([]int{i})
   385  
   386  			if sw, ok := w.(StructWalker); ok {
   387  				err = sw.StructField(sf, f)
   388  
   389  				// SkipEntry just pretends this field doesn't even exist
   390  				if err == SkipEntry {
   391  					continue
   392  				}
   393  
   394  				if err != nil {
   395  					return
   396  				}
   397  			}
   398  
   399  			ew, ok := w.(EnterExitWalker)
   400  			if ok {
   401  				ew.Enter(StructField)
   402  			}
   403  
   404  			err = walk(f, w)
   405  			if err != nil {
   406  				return
   407  			}
   408  
   409  			if ok {
   410  				ew.Exit(StructField)
   411  			}
   412  		}
   413  	}
   414  
   415  	if ewok {
   416  		ew.Exit(Struct)
   417  	}
   418  
   419  	return nil
   420  }
   421  

View as plain text