2
Copyright (c) 2014 VMware, Inc. All Rights Reserved.
4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
8
http://www.apache.org/licenses/LICENSE-2.0
10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
24
type WalkFn func(c interface{}) error
26
// Walk recursively walks struct types that implement the specified interface.
27
// Fields that implement the specified interface are expected to be pointer
28
// values. This allows the function to cache pointer values on a per-type
29
// basis. If, during a recursive walk, the same type is encountered twice, the
30
// function creates a new value of that type the first time, and reuses that
31
// same value the second time.
33
// This function is used to make sure that a hierarchy of flags where multiple
34
// structs refer to the `Client` flag will not end up with more than one
35
// instance of the actual client. Rather, every struct referring to the
36
// `Client` flag will have a pointer to the same underlying `Client` struct.
38
func Walk(c interface{}, ifaceType reflect.Type, fn WalkFn) error {
41
visited := make(map[reflect.Type]reflect.Value)
42
walker = func(c interface{}) error {
43
v := reflect.ValueOf(c)
44
if v.Kind() == reflect.Interface {
47
if v.Kind() == reflect.Ptr {
53
// Call user specified function.
59
for i := 0; i < t.NumField(); i++ {
64
// Check that a pointer to this field's type doesn't implement the
65
// specified interface. If it does, this field references the type as
66
// value. This is not allowed because it prohibits a value from being
67
// shared among multiple structs that reference it.
69
// For example: if a struct has two fields of the same type, they must
70
// both point to the same value after this routine has executed. If these
71
// fields are not a pointer type, the value cannot be shared.
73
if reflect.PtrTo(ft).Implements(ifaceType) {
74
panic(fmt.Sprintf(`field "%s" in struct "%s" must be a pointer`, ff.Name, v.Type()))
77
// Type must implement specified interface.
78
if !ft.Implements(ifaceType) {
82
// Type must be a pointer.
83
if ft.Kind() != reflect.Ptr {
84
panic(fmt.Sprintf(`field "%s" in struct "%s" must be a pointer`, ff.Name, v.Type()))
87
// Have not seen this type before, create a value.
88
if _, ok := visited[ft]; !ok {
89
visited[ft] = reflect.New(ft.Elem())
92
// Make sure current field is set.
98
err := walker(fv.Interface())