aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/zclconf/go-cty/cty/walk.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/walk.go')
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/walk.go182
1 files changed, 182 insertions, 0 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/walk.go b/vendor/github.com/zclconf/go-cty/cty/walk.go
new file mode 100644
index 0000000..a6943ba
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/walk.go
@@ -0,0 +1,182 @@
1package cty
2
3// Walk visits all of the values in a possibly-complex structure, calling
4// a given function for each value.
5//
6// For example, given a list of strings the callback would first be called
7// with the whole list and then called once for each element of the list.
8//
9// The callback function may prevent recursive visits to child values by
10// returning false. The callback function my halt the walk altogether by
11// returning a non-nil error. If the returned error is about the element
12// currently being visited, it is recommended to use the provided path
13// value to produce a PathError describing that context.
14//
15// The path passed to the given function may not be used after that function
16// returns, since its backing array is re-used for other calls.
17func Walk(val Value, cb func(Path, Value) (bool, error)) error {
18 var path Path
19 return walk(path, val, cb)
20}
21
22func walk(path Path, val Value, cb func(Path, Value) (bool, error)) error {
23 deeper, err := cb(path, val)
24 if err != nil {
25 return err
26 }
27 if !deeper {
28 return nil
29 }
30
31 if val.IsNull() || !val.IsKnown() {
32 // Can't recurse into null or unknown values, regardless of type
33 return nil
34 }
35
36 ty := val.Type()
37 switch {
38 case ty.IsObjectType():
39 for it := val.ElementIterator(); it.Next(); {
40 nameVal, av := it.Element()
41 path := append(path, GetAttrStep{
42 Name: nameVal.AsString(),
43 })
44 err := walk(path, av, cb)
45 if err != nil {
46 return err
47 }
48 }
49 case val.CanIterateElements():
50 for it := val.ElementIterator(); it.Next(); {
51 kv, ev := it.Element()
52 path := append(path, IndexStep{
53 Key: kv,
54 })
55 err := walk(path, ev, cb)
56 if err != nil {
57 return err
58 }
59 }
60 }
61 return nil
62}
63
64// Transform visits all of the values in a possibly-complex structure,
65// calling a given function for each value which has an opportunity to
66// replace that value.
67//
68// Unlike Walk, Transform visits child nodes first, so for a list of strings
69// it would first visit the strings and then the _new_ list constructed
70// from the transformed values of the list items.
71//
72// This is useful for creating the effect of being able to make deep mutations
73// to a value even though values are immutable. However, it's the responsibility
74// of the given function to preserve expected invariants, such as homogenity of
75// element types in collections; this function can panic if such invariants
76// are violated, just as if new values were constructed directly using the
77// value constructor functions. An easy way to preserve invariants is to
78// ensure that the transform function never changes the value type.
79//
80// The callback function my halt the walk altogether by
81// returning a non-nil error. If the returned error is about the element
82// currently being visited, it is recommended to use the provided path
83// value to produce a PathError describing that context.
84//
85// The path passed to the given function may not be used after that function
86// returns, since its backing array is re-used for other calls.
87func Transform(val Value, cb func(Path, Value) (Value, error)) (Value, error) {
88 var path Path
89 return transform(path, val, cb)
90}
91
92func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value, error) {
93 ty := val.Type()
94 var newVal Value
95
96 switch {
97
98 case val.IsNull() || !val.IsKnown():
99 // Can't recurse into null or unknown values, regardless of type
100 newVal = val
101
102 case ty.IsListType() || ty.IsSetType() || ty.IsTupleType():
103 l := val.LengthInt()
104 switch l {
105 case 0:
106 // No deep transform for an empty sequence
107 newVal = val
108 default:
109 elems := make([]Value, 0, l)
110 for it := val.ElementIterator(); it.Next(); {
111 kv, ev := it.Element()
112 path := append(path, IndexStep{
113 Key: kv,
114 })
115 newEv, err := transform(path, ev, cb)
116 if err != nil {
117 return DynamicVal, err
118 }
119 elems = append(elems, newEv)
120 }
121 switch {
122 case ty.IsListType():
123 newVal = ListVal(elems)
124 case ty.IsSetType():
125 newVal = SetVal(elems)
126 case ty.IsTupleType():
127 newVal = TupleVal(elems)
128 default:
129 panic("unknown sequence type") // should never happen because of the case we are in
130 }
131 }
132
133 case ty.IsMapType():
134 l := val.LengthInt()
135 switch l {
136 case 0:
137 // No deep transform for an empty map
138 newVal = val
139 default:
140 elems := make(map[string]Value)
141 for it := val.ElementIterator(); it.Next(); {
142 kv, ev := it.Element()
143 path := append(path, IndexStep{
144 Key: kv,
145 })
146 newEv, err := transform(path, ev, cb)
147 if err != nil {
148 return DynamicVal, err
149 }
150 elems[kv.AsString()] = newEv
151 }
152 newVal = MapVal(elems)
153 }
154
155 case ty.IsObjectType():
156 switch {
157 case ty.Equals(EmptyObject):
158 // No deep transform for an empty object
159 newVal = val
160 default:
161 atys := ty.AttributeTypes()
162 newAVs := make(map[string]Value)
163 for name := range atys {
164 av := val.GetAttr(name)
165 path := append(path, GetAttrStep{
166 Name: name,
167 })
168 newAV, err := transform(path, av, cb)
169 if err != nil {
170 return DynamicVal, err
171 }
172 newAVs[name] = newAV
173 }
174 newVal = ObjectVal(newAVs)
175 }
176
177 default:
178 newVal = val
179 }
180
181 return cb(path, newVal)
182}