forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/mitchellh/reflectwalk/reflectwalk.go

677 lines
8.8 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
// reflectwalk is a package that allows you to "walk" complex structures
2024-02-18 10:42:21 +00:00
// similar to how you may "walk" a filesystem: visiting every element one
2024-02-18 10:42:21 +00:00
// by one and calling callback functions allowing you to handle and manipulate
2024-02-18 10:42:21 +00:00
// those elements.
2024-02-18 10:42:21 +00:00
package reflectwalk
import (
"errors"
"reflect"
)
// PrimitiveWalker implementations are able to handle primitive values
2024-02-18 10:42:21 +00:00
// within complex structures. Primitive values are numbers, strings,
2024-02-18 10:42:21 +00:00
// booleans, funcs, chans.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// These primitive values are often members of more complex
2024-02-18 10:42:21 +00:00
// structures (slices, maps, etc.) that are walkable by other interfaces.
2024-02-18 10:42:21 +00:00
type PrimitiveWalker interface {
Primitive(reflect.Value) error
}
// InterfaceWalker implementations are able to handle interface values as they
2024-02-18 10:42:21 +00:00
// are encountered during the walk.
2024-02-18 10:42:21 +00:00
type InterfaceWalker interface {
Interface(reflect.Value) error
}
// MapWalker implementations are able to handle individual elements
2024-02-18 10:42:21 +00:00
// found within a map structure.
2024-02-18 10:42:21 +00:00
type MapWalker interface {
Map(m reflect.Value) error
2024-02-18 10:42:21 +00:00
MapElem(m, k, v reflect.Value) error
}
// SliceWalker implementations are able to handle slice elements found
2024-02-18 10:42:21 +00:00
// within complex structures.
2024-02-18 10:42:21 +00:00
type SliceWalker interface {
Slice(reflect.Value) error
2024-02-18 10:42:21 +00:00
SliceElem(int, reflect.Value) error
}
// ArrayWalker implementations are able to handle array elements found
2024-02-18 10:42:21 +00:00
// within complex structures.
2024-02-18 10:42:21 +00:00
type ArrayWalker interface {
Array(reflect.Value) error
2024-02-18 10:42:21 +00:00
ArrayElem(int, reflect.Value) error
}
// StructWalker is an interface that has methods that are called for
2024-02-18 10:42:21 +00:00
// structs when a Walk is done.
2024-02-18 10:42:21 +00:00
type StructWalker interface {
Struct(reflect.Value) error
2024-02-18 10:42:21 +00:00
StructField(reflect.StructField, reflect.Value) error
}
// EnterExitWalker implementations are notified before and after
2024-02-18 10:42:21 +00:00
// they walk deeper into complex structures (into struct fields,
2024-02-18 10:42:21 +00:00
// into slice elements, etc.)
2024-02-18 10:42:21 +00:00
type EnterExitWalker interface {
Enter(Location) error
2024-02-18 10:42:21 +00:00
Exit(Location) error
}
// PointerWalker implementations are notified when the value they're
2024-02-18 10:42:21 +00:00
// walking is a pointer or not. Pointer is called for _every_ value whether
2024-02-18 10:42:21 +00:00
// it is a pointer or not.
2024-02-18 10:42:21 +00:00
type PointerWalker interface {
PointerEnter(bool) error
2024-02-18 10:42:21 +00:00
PointerExit(bool) error
}
// PointerValueWalker implementations are notified with the value of
2024-02-18 10:42:21 +00:00
// a particular pointer when a pointer is walked. Pointer is called
2024-02-18 10:42:21 +00:00
// right before PointerEnter.
2024-02-18 10:42:21 +00:00
type PointerValueWalker interface {
Pointer(reflect.Value) error
}
// SkipEntry can be returned from walk functions to skip walking
2024-02-18 10:42:21 +00:00
// the value of this field. This is only valid in the following functions:
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// - Struct: skips all fields from being walked
2024-02-18 10:42:21 +00:00
// - StructField: skips walking the struct value
2024-02-18 10:42:21 +00:00
var SkipEntry = errors.New("skip this entry")
// Walk takes an arbitrary value and an interface and traverses the
2024-02-18 10:42:21 +00:00
// value, calling callbacks on the interface if they are supported.
2024-02-18 10:42:21 +00:00
// The interface should implement one or more of the walker interfaces
2024-02-18 10:42:21 +00:00
// in this package, such as PrimitiveWalker, StructWalker, etc.
2024-02-18 10:42:21 +00:00
func Walk(data, walker interface{}) (err error) {
2024-02-18 10:42:21 +00:00
v := reflect.ValueOf(data)
2024-02-18 10:42:21 +00:00
ew, ok := walker.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
err = ew.Enter(WalkLoc)
2024-02-18 10:42:21 +00:00
}
if err == nil {
2024-02-18 10:42:21 +00:00
err = walk(v, walker)
2024-02-18 10:42:21 +00:00
}
if ok && err == nil {
2024-02-18 10:42:21 +00:00
err = ew.Exit(WalkLoc)
2024-02-18 10:42:21 +00:00
}
return
2024-02-18 10:42:21 +00:00
}
func walk(v reflect.Value, w interface{}) (err error) {
2024-02-18 10:42:21 +00:00
// Determine if we're receiving a pointer and if so notify the walker.
2024-02-18 10:42:21 +00:00
// The logic here is convoluted but very important (tests will fail if
2024-02-18 10:42:21 +00:00
// almost any part is changed). I will try to explain here.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// First, we check if the value is an interface, if so, we really need
2024-02-18 10:42:21 +00:00
// to check the interface's VALUE to see whether it is a pointer.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Check whether the value is then a pointer. If so, then set pointer
2024-02-18 10:42:21 +00:00
// to true to notify the user.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// If we still have a pointer or an interface after the indirections, then
2024-02-18 10:42:21 +00:00
// we unwrap another level
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// At this time, we also set "v" to be the dereferenced value. This is
2024-02-18 10:42:21 +00:00
// because once we've unwrapped the pointer we want to use that value.
2024-02-18 10:42:21 +00:00
pointer := false
2024-02-18 10:42:21 +00:00
pointerV := v
for {
2024-02-18 10:42:21 +00:00
if pointerV.Kind() == reflect.Interface {
2024-02-18 10:42:21 +00:00
if iw, ok := w.(InterfaceWalker); ok {
2024-02-18 10:42:21 +00:00
if err = iw.Interface(pointerV); err != nil {
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
pointerV = pointerV.Elem()
2024-02-18 10:42:21 +00:00
}
if pointerV.Kind() == reflect.Ptr {
2024-02-18 10:42:21 +00:00
if pw, ok := w.(PointerValueWalker); ok {
2024-02-18 10:42:21 +00:00
if err = pw.Pointer(pointerV); err != nil {
2024-02-18 10:42:21 +00:00
if err == SkipEntry {
2024-02-18 10:42:21 +00:00
// Skip the rest of this entry but clear the error
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
return
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
pointer = true
2024-02-18 10:42:21 +00:00
v = reflect.Indirect(pointerV)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if pw, ok := w.(PointerWalker); ok {
2024-02-18 10:42:21 +00:00
if err = pw.PointerEnter(pointer); err != nil {
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
defer func(pointer bool) {
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
err = pw.PointerExit(pointer)
2024-02-18 10:42:21 +00:00
}(pointer)
2024-02-18 10:42:21 +00:00
}
if pointer {
2024-02-18 10:42:21 +00:00
pointerV = v
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pointer = false
// If we still have a pointer or interface we have to indirect another level.
2024-02-18 10:42:21 +00:00
switch pointerV.Kind() {
2024-02-18 10:42:21 +00:00
case reflect.Ptr, reflect.Interface:
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
break
2024-02-18 10:42:21 +00:00
}
// We preserve the original value here because if it is an interface
2024-02-18 10:42:21 +00:00
// type, we want to pass that directly into the walkPrimitive, so that
2024-02-18 10:42:21 +00:00
// we can set it.
2024-02-18 10:42:21 +00:00
originalV := v
2024-02-18 10:42:21 +00:00
if v.Kind() == reflect.Interface {
2024-02-18 10:42:21 +00:00
v = v.Elem()
2024-02-18 10:42:21 +00:00
}
k := v.Kind()
2024-02-18 10:42:21 +00:00
if k >= reflect.Int && k <= reflect.Complex128 {
2024-02-18 10:42:21 +00:00
k = reflect.Int
2024-02-18 10:42:21 +00:00
}
switch k {
2024-02-18 10:42:21 +00:00
// Primitives
2024-02-18 10:42:21 +00:00
case reflect.Bool, reflect.Chan, reflect.Func, reflect.Int, reflect.String, reflect.Invalid:
2024-02-18 10:42:21 +00:00
err = walkPrimitive(originalV, w)
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
case reflect.Map:
2024-02-18 10:42:21 +00:00
err = walkMap(v, w)
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
case reflect.Slice:
2024-02-18 10:42:21 +00:00
err = walkSlice(v, w)
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
case reflect.Struct:
2024-02-18 10:42:21 +00:00
err = walkStruct(v, w)
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
case reflect.Array:
2024-02-18 10:42:21 +00:00
err = walkArray(v, w)
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
panic("unsupported type: " + k.String())
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
func walkMap(v reflect.Value, w interface{}) error {
2024-02-18 10:42:21 +00:00
ew, ewok := w.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ewok {
2024-02-18 10:42:21 +00:00
ew.Enter(Map)
2024-02-18 10:42:21 +00:00
}
if mw, ok := w.(MapWalker); ok {
2024-02-18 10:42:21 +00:00
if err := mw.Map(v); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
for _, k := range v.MapKeys() {
2024-02-18 10:42:21 +00:00
kv := v.MapIndex(k)
if mw, ok := w.(MapWalker); ok {
2024-02-18 10:42:21 +00:00
if err := mw.MapElem(v, k, kv); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
ew, ok := w.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
ew.Enter(MapKey)
2024-02-18 10:42:21 +00:00
}
if err := walk(k, w); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
if ok {
2024-02-18 10:42:21 +00:00
ew.Exit(MapKey)
2024-02-18 10:42:21 +00:00
ew.Enter(MapValue)
2024-02-18 10:42:21 +00:00
}
// get the map value again as it may have changed in the MapElem call
2024-02-18 10:42:21 +00:00
if err := walk(v.MapIndex(k), w); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
if ok {
2024-02-18 10:42:21 +00:00
ew.Exit(MapValue)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
if ewok {
2024-02-18 10:42:21 +00:00
ew.Exit(Map)
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}
func walkPrimitive(v reflect.Value, w interface{}) error {
2024-02-18 10:42:21 +00:00
if pw, ok := w.(PrimitiveWalker); ok {
2024-02-18 10:42:21 +00:00
return pw.Primitive(v)
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}
func walkSlice(v reflect.Value, w interface{}) (err error) {
2024-02-18 10:42:21 +00:00
ew, ok := w.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
ew.Enter(Slice)
2024-02-18 10:42:21 +00:00
}
if sw, ok := w.(SliceWalker); ok {
2024-02-18 10:42:21 +00:00
if err := sw.Slice(v); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
for i := 0; i < v.Len(); i++ {
2024-02-18 10:42:21 +00:00
elem := v.Index(i)
if sw, ok := w.(SliceWalker); ok {
2024-02-18 10:42:21 +00:00
if err := sw.SliceElem(i, elem); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
ew, ok := w.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
ew.Enter(SliceElem)
2024-02-18 10:42:21 +00:00
}
if err := walk(elem, w); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
if ok {
2024-02-18 10:42:21 +00:00
ew.Exit(SliceElem)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
ew, ok = w.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
ew.Exit(Slice)
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}
func walkArray(v reflect.Value, w interface{}) (err error) {
2024-02-18 10:42:21 +00:00
ew, ok := w.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
ew.Enter(Array)
2024-02-18 10:42:21 +00:00
}
if aw, ok := w.(ArrayWalker); ok {
2024-02-18 10:42:21 +00:00
if err := aw.Array(v); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
for i := 0; i < v.Len(); i++ {
2024-02-18 10:42:21 +00:00
elem := v.Index(i)
if aw, ok := w.(ArrayWalker); ok {
2024-02-18 10:42:21 +00:00
if err := aw.ArrayElem(i, elem); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
ew, ok := w.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
ew.Enter(ArrayElem)
2024-02-18 10:42:21 +00:00
}
if err := walk(elem, w); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
if ok {
2024-02-18 10:42:21 +00:00
ew.Exit(ArrayElem)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
ew, ok = w.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
ew.Exit(Array)
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}
func walkStruct(v reflect.Value, w interface{}) (err error) {
2024-02-18 10:42:21 +00:00
ew, ewok := w.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ewok {
2024-02-18 10:42:21 +00:00
ew.Enter(Struct)
2024-02-18 10:42:21 +00:00
}
skip := false
2024-02-18 10:42:21 +00:00
if sw, ok := w.(StructWalker); ok {
2024-02-18 10:42:21 +00:00
err = sw.Struct(v)
2024-02-18 10:42:21 +00:00
if err == SkipEntry {
2024-02-18 10:42:21 +00:00
skip = true
2024-02-18 10:42:21 +00:00
err = nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
if !skip {
2024-02-18 10:42:21 +00:00
vt := v.Type()
2024-02-18 10:42:21 +00:00
for i := 0; i < vt.NumField(); i++ {
2024-02-18 10:42:21 +00:00
sf := vt.Field(i)
2024-02-18 10:42:21 +00:00
f := v.FieldByIndex([]int{i})
if sw, ok := w.(StructWalker); ok {
2024-02-18 10:42:21 +00:00
err = sw.StructField(sf, f)
// SkipEntry just pretends this field doesn't even exist
2024-02-18 10:42:21 +00:00
if err == SkipEntry {
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
if err != nil {
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
ew, ok := w.(EnterExitWalker)
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
ew.Enter(StructField)
2024-02-18 10:42:21 +00:00
}
err = walk(f, w)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
if ok {
2024-02-18 10:42:21 +00:00
ew.Exit(StructField)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
if ewok {
2024-02-18 10:42:21 +00:00
ew.Exit(Struct)
2024-02-18 10:42:21 +00:00
}
return nil
2024-02-18 10:42:21 +00:00
}