// Package structwalk allows you to "walk" the hierarchy of a struct. It is1// very similar to github.com/mitchellh/reflectwalk but allows you to change2// the visitor mid-walk.3package structwalk45import (6"reflect"78"github.com/mitchellh/reflectwalk"9)1011// Walk traverses the hierarchy of o in depth-first order. It starts by calling12// v.Visit(o). If the visitor w returned by v.Visit(o) is not nil, Walk is13// invoked recursively with visitor w for each of the structs inside of o,14// followed by a call to w.Visit(nil).15//16// o must be non-nil.17func Walk(v Visitor, o interface{}) {18sw := structWalker{v: v}19_ = reflectwalk.Walk(o, &sw)20}2122// Visitor will have its Visit method invoked for each struct value encountered23// by Walk. If w returned from Visit is non-nil, Walk will then visit each child24// of value with w. The final call after visiting all children will be to25// w.Visit(nil).26type Visitor interface {27Visit(value interface{}) (w Visitor)28}2930type structWalker struct {31cur interface{}32v Visitor33}3435// Struct invoke the Visitor for v and its children.36func (sw *structWalker) Struct(v reflect.Value) error {37// structWalker will walk absolutely all fields, even unexported fields or38// types. We can only interface exported fields, so we need to abort early39// for anything that's not supported.40if !v.CanInterface() {41return nil42}4344// Get the interface to the value. reflectwalk will fully derefernce all45// structs, so if it's possible for us to get address it into a pointer,46// we will use that for visiting.47var (48rawValue = v.Interface()49ptrValue = rawValue50)51if v.Kind() != reflect.Ptr && v.CanAddr() {52ptrValue = v.Addr().Interface()53}5455// Struct will recursively call reflectwalk.Walk with a new walker, which56// means that sw.Struct will be called twice for the same value. We want57// to ignore calls to Struct with the same value so we don't recurse58// infinitely.59if sw.cur != nil && reflect.DeepEqual(rawValue, sw.cur) {60return nil61}6263// Visit our struct and create a new walker with the returned Visitor.64w := sw.v.Visit(ptrValue)65if w == nil {66return reflectwalk.SkipEntry67}68_ = reflectwalk.Walk(rawValue, &structWalker{cur: rawValue, v: w})69w.Visit(nil)7071return reflectwalk.SkipEntry72}7374func (sw *structWalker) StructField(reflect.StructField, reflect.Value) error {75return nil76}777879