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