~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/govmomi/govc/cli/walk.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2014 VMware, Inc. All Rights Reserved.
 
3
 
 
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
 
7
 
 
8
    http://www.apache.org/licenses/LICENSE-2.0
 
9
 
 
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.
 
15
*/
 
16
 
 
17
package cli
 
18
 
 
19
import (
 
20
        "fmt"
 
21
        "reflect"
 
22
)
 
23
 
 
24
type WalkFn func(c interface{}) error
 
25
 
 
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.
 
32
//
 
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.
 
37
//
 
38
func Walk(c interface{}, ifaceType reflect.Type, fn WalkFn) error {
 
39
        var walker WalkFn
 
40
 
 
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 {
 
45
                        v = v.Elem()
 
46
                }
 
47
                if v.Kind() == reflect.Ptr {
 
48
                        v = v.Elem()
 
49
                }
 
50
 
 
51
                t := v.Type()
 
52
 
 
53
                // Call user specified function.
 
54
                err := fn(c)
 
55
                if err != nil {
 
56
                        return err
 
57
                }
 
58
 
 
59
                for i := 0; i < t.NumField(); i++ {
 
60
                        ff := t.Field(i)
 
61
                        ft := ff.Type
 
62
                        fv := v.Field(i)
 
63
 
 
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.
 
68
                        //
 
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.
 
72
                        //
 
73
                        if reflect.PtrTo(ft).Implements(ifaceType) {
 
74
                                panic(fmt.Sprintf(`field "%s" in struct "%s" must be a pointer`, ff.Name, v.Type()))
 
75
                        }
 
76
 
 
77
                        // Type must implement specified interface.
 
78
                        if !ft.Implements(ifaceType) {
 
79
                                continue
 
80
                        }
 
81
 
 
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()))
 
85
                        }
 
86
 
 
87
                        // Have not seen this type before, create a value.
 
88
                        if _, ok := visited[ft]; !ok {
 
89
                                visited[ft] = reflect.New(ft.Elem())
 
90
                        }
 
91
 
 
92
                        // Make sure current field is set.
 
93
                        if fv.IsNil() {
 
94
                                fv.Set(visited[ft])
 
95
                        }
 
96
 
 
97
                        // Recurse.
 
98
                        err := walker(fv.Interface())
 
99
                        if err != nil {
 
100
                                return err
 
101
                        }
 
102
                }
 
103
 
 
104
                return nil
 
105
        }
 
106
 
 
107
        return walker(c)
 
108
}