Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/util/structwalk/structwalk.go
4094 views
1
// Package structwalk allows you to "walk" the hierarchy of a struct. It is
2
// very similar to github.com/mitchellh/reflectwalk but allows you to change
3
// the visitor mid-walk.
4
package structwalk
5
6
import (
7
"reflect"
8
9
"github.com/mitchellh/reflectwalk"
10
)
11
12
// Walk traverses the hierarchy of o in depth-first order. It starts by calling
13
// v.Visit(o). If the visitor w returned by v.Visit(o) is not nil, Walk is
14
// invoked recursively with visitor w for each of the structs inside of o,
15
// followed by a call to w.Visit(nil).
16
//
17
// o must be non-nil.
18
func Walk(v Visitor, o interface{}) {
19
sw := structWalker{v: v}
20
_ = reflectwalk.Walk(o, &sw)
21
}
22
23
// Visitor will have its Visit method invoked for each struct value encountered
24
// by Walk. If w returned from Visit is non-nil, Walk will then visit each child
25
// of value with w. The final call after visiting all children will be to
26
// w.Visit(nil).
27
type Visitor interface {
28
Visit(value interface{}) (w Visitor)
29
}
30
31
type structWalker struct {
32
cur interface{}
33
v Visitor
34
}
35
36
// Struct invoke the Visitor for v and its children.
37
func (sw *structWalker) Struct(v reflect.Value) error {
38
// structWalker will walk absolutely all fields, even unexported fields or
39
// types. We can only interface exported fields, so we need to abort early
40
// for anything that's not supported.
41
if !v.CanInterface() {
42
return nil
43
}
44
45
// Get the interface to the value. reflectwalk will fully derefernce all
46
// structs, so if it's possible for us to get address it into a pointer,
47
// we will use that for visiting.
48
var (
49
rawValue = v.Interface()
50
ptrValue = rawValue
51
)
52
if v.Kind() != reflect.Ptr && v.CanAddr() {
53
ptrValue = v.Addr().Interface()
54
}
55
56
// Struct will recursively call reflectwalk.Walk with a new walker, which
57
// means that sw.Struct will be called twice for the same value. We want
58
// to ignore calls to Struct with the same value so we don't recurse
59
// infinitely.
60
if sw.cur != nil && reflect.DeepEqual(rawValue, sw.cur) {
61
return nil
62
}
63
64
// Visit our struct and create a new walker with the returned Visitor.
65
w := sw.v.Visit(ptrValue)
66
if w == nil {
67
return reflectwalk.SkipEntry
68
}
69
_ = reflectwalk.Walk(rawValue, &structWalker{cur: rawValue, v: w})
70
w.Visit(nil)
71
72
return reflectwalk.SkipEntry
73
}
74
75
func (sw *structWalker) StructField(reflect.StructField, reflect.Value) error {
76
return nil
77
}
78
79