diff options
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/walk.go')
-rw-r--r-- | vendor/github.com/zclconf/go-cty/cty/walk.go | 182 |
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 @@ | |||
1 | package 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. | ||
17 | func Walk(val Value, cb func(Path, Value) (bool, error)) error { | ||
18 | var path Path | ||
19 | return walk(path, val, cb) | ||
20 | } | ||
21 | |||
22 | func 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. | ||
87 | func Transform(val Value, cb func(Path, Value) (Value, error)) (Value, error) { | ||
88 | var path Path | ||
89 | return transform(path, val, cb) | ||
90 | } | ||
91 | |||
92 | func 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 | } | ||