]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package convert |
2 | ||
3 | import ( | |
4 | "github.com/zclconf/go-cty/cty" | |
5 | ) | |
6 | ||
7 | // conversionCollectionToList returns a conversion that will apply the given | |
8 | // conversion to all of the elements of a collection (something that supports | |
9 | // ForEachElement and LengthInt) and then returns the result as a list. | |
10 | // | |
11 | // "conv" can be nil if the elements are expected to already be of the | |
12 | // correct type and just need to be re-wrapped into a list. (For example, | |
13 | // if we're converting from a set into a list of the same element type.) | |
14 | func conversionCollectionToList(ety cty.Type, conv conversion) conversion { | |
15 | return func(val cty.Value, path cty.Path) (cty.Value, error) { | |
16 | elems := make([]cty.Value, 0, val.LengthInt()) | |
17 | i := int64(0) | |
18 | path = append(path, nil) | |
19 | it := val.ElementIterator() | |
20 | for it.Next() { | |
21 | _, val := it.Element() | |
22 | var err error | |
23 | ||
24 | path[len(path)-1] = cty.IndexStep{ | |
25 | Key: cty.NumberIntVal(i), | |
26 | } | |
27 | ||
28 | if conv != nil { | |
29 | val, err = conv(val, path) | |
30 | if err != nil { | |
31 | return cty.NilVal, err | |
32 | } | |
33 | } | |
34 | elems = append(elems, val) | |
35 | ||
36 | i++ | |
37 | } | |
38 | ||
39 | if len(elems) == 0 { | |
40 | return cty.ListValEmpty(ety), nil | |
41 | } | |
42 | ||
43 | return cty.ListVal(elems), nil | |
44 | } | |
45 | } | |
46 | ||
47 | // conversionCollectionToSet returns a conversion that will apply the given | |
48 | // conversion to all of the elements of a collection (something that supports | |
49 | // ForEachElement and LengthInt) and then returns the result as a set. | |
50 | // | |
51 | // "conv" can be nil if the elements are expected to already be of the | |
52 | // correct type and just need to be re-wrapped into a set. (For example, | |
53 | // if we're converting from a list into a set of the same element type.) | |
54 | func conversionCollectionToSet(ety cty.Type, conv conversion) conversion { | |
55 | return func(val cty.Value, path cty.Path) (cty.Value, error) { | |
56 | elems := make([]cty.Value, 0, val.LengthInt()) | |
57 | i := int64(0) | |
58 | path = append(path, nil) | |
59 | it := val.ElementIterator() | |
60 | for it.Next() { | |
61 | _, val := it.Element() | |
62 | var err error | |
63 | ||
64 | path[len(path)-1] = cty.IndexStep{ | |
65 | Key: cty.NumberIntVal(i), | |
66 | } | |
67 | ||
68 | if conv != nil { | |
69 | val, err = conv(val, path) | |
70 | if err != nil { | |
71 | return cty.NilVal, err | |
72 | } | |
73 | } | |
74 | elems = append(elems, val) | |
75 | ||
76 | i++ | |
77 | } | |
78 | ||
79 | if len(elems) == 0 { | |
80 | return cty.SetValEmpty(ety), nil | |
81 | } | |
82 | ||
83 | return cty.SetVal(elems), nil | |
84 | } | |
85 | } | |
86 | ||
107c1cdb ND |
87 | // conversionCollectionToMap returns a conversion that will apply the given |
88 | // conversion to all of the elements of a collection (something that supports | |
89 | // ForEachElement and LengthInt) and then returns the result as a map. | |
90 | // | |
91 | // "conv" can be nil if the elements are expected to already be of the | |
92 | // correct type and just need to be re-wrapped into a map. | |
93 | func conversionCollectionToMap(ety cty.Type, conv conversion) conversion { | |
94 | return func(val cty.Value, path cty.Path) (cty.Value, error) { | |
95 | elems := make(map[string]cty.Value, 0) | |
96 | path = append(path, nil) | |
97 | it := val.ElementIterator() | |
98 | for it.Next() { | |
99 | key, val := it.Element() | |
100 | var err error | |
101 | ||
102 | path[len(path)-1] = cty.IndexStep{ | |
103 | Key: key, | |
104 | } | |
105 | ||
106 | keyStr, err := Convert(key, cty.String) | |
107 | if err != nil { | |
108 | // Should never happen, because keys can only be numbers or | |
109 | // strings and both can convert to string. | |
110 | return cty.DynamicVal, path.NewErrorf("cannot convert key type %s to string for map", key.Type().FriendlyName()) | |
111 | } | |
112 | ||
113 | if conv != nil { | |
114 | val, err = conv(val, path) | |
115 | if err != nil { | |
116 | return cty.NilVal, err | |
117 | } | |
118 | } | |
119 | ||
120 | elems[keyStr.AsString()] = val | |
121 | } | |
122 | ||
123 | if len(elems) == 0 { | |
124 | return cty.MapValEmpty(ety), nil | |
125 | } | |
126 | ||
127 | return cty.MapVal(elems), nil | |
128 | } | |
129 | } | |
130 | ||
131 | // conversionTupleToSet returns a conversion that will take a value of the | |
132 | // given tuple type and return a set of the given element type. | |
133 | // | |
134 | // Will panic if the given tupleType isn't actually a tuple type. | |
135 | func conversionTupleToSet(tupleType cty.Type, listEty cty.Type, unsafe bool) conversion { | |
136 | tupleEtys := tupleType.TupleElementTypes() | |
137 | ||
138 | if len(tupleEtys) == 0 { | |
139 | // Empty tuple short-circuit | |
140 | return func(val cty.Value, path cty.Path) (cty.Value, error) { | |
141 | return cty.SetValEmpty(listEty), nil | |
142 | } | |
143 | } | |
144 | ||
145 | if listEty == cty.DynamicPseudoType { | |
146 | // This is a special case where the caller wants us to find | |
147 | // a suitable single type that all elements can convert to, if | |
148 | // possible. | |
149 | listEty, _ = unify(tupleEtys, unsafe) | |
150 | if listEty == cty.NilType { | |
151 | return nil | |
152 | } | |
153 | } | |
154 | ||
155 | elemConvs := make([]conversion, len(tupleEtys)) | |
156 | for i, tupleEty := range tupleEtys { | |
157 | if tupleEty.Equals(listEty) { | |
158 | // no conversion required | |
159 | continue | |
160 | } | |
161 | ||
162 | elemConvs[i] = getConversion(tupleEty, listEty, unsafe) | |
163 | if elemConvs[i] == nil { | |
164 | // If any of our element conversions are impossible, then the our | |
165 | // whole conversion is impossible. | |
166 | return nil | |
167 | } | |
168 | } | |
169 | ||
170 | // If we fall out here then a conversion is possible, using the | |
171 | // element conversions in elemConvs | |
172 | return func(val cty.Value, path cty.Path) (cty.Value, error) { | |
173 | elems := make([]cty.Value, 0, len(elemConvs)) | |
174 | path = append(path, nil) | |
175 | i := int64(0) | |
176 | it := val.ElementIterator() | |
177 | for it.Next() { | |
178 | _, val := it.Element() | |
179 | var err error | |
180 | ||
181 | path[len(path)-1] = cty.IndexStep{ | |
182 | Key: cty.NumberIntVal(i), | |
183 | } | |
184 | ||
185 | conv := elemConvs[i] | |
186 | if conv != nil { | |
187 | val, err = conv(val, path) | |
188 | if err != nil { | |
189 | return cty.NilVal, err | |
190 | } | |
191 | } | |
192 | elems = append(elems, val) | |
193 | ||
194 | i++ | |
195 | } | |
196 | ||
197 | return cty.SetVal(elems), nil | |
198 | } | |
199 | } | |
200 | ||
15c0b25d AP |
201 | // conversionTupleToList returns a conversion that will take a value of the |
202 | // given tuple type and return a list of the given element type. | |
203 | // | |
204 | // Will panic if the given tupleType isn't actually a tuple type. | |
205 | func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) conversion { | |
206 | tupleEtys := tupleType.TupleElementTypes() | |
207 | ||
208 | if len(tupleEtys) == 0 { | |
209 | // Empty tuple short-circuit | |
210 | return func(val cty.Value, path cty.Path) (cty.Value, error) { | |
211 | return cty.ListValEmpty(listEty), nil | |
212 | } | |
213 | } | |
214 | ||
215 | if listEty == cty.DynamicPseudoType { | |
216 | // This is a special case where the caller wants us to find | |
217 | // a suitable single type that all elements can convert to, if | |
218 | // possible. | |
219 | listEty, _ = unify(tupleEtys, unsafe) | |
220 | if listEty == cty.NilType { | |
221 | return nil | |
222 | } | |
223 | } | |
224 | ||
225 | elemConvs := make([]conversion, len(tupleEtys)) | |
226 | for i, tupleEty := range tupleEtys { | |
227 | if tupleEty.Equals(listEty) { | |
228 | // no conversion required | |
229 | continue | |
230 | } | |
231 | ||
232 | elemConvs[i] = getConversion(tupleEty, listEty, unsafe) | |
233 | if elemConvs[i] == nil { | |
234 | // If any of our element conversions are impossible, then the our | |
235 | // whole conversion is impossible. | |
236 | return nil | |
237 | } | |
238 | } | |
239 | ||
240 | // If we fall out here then a conversion is possible, using the | |
241 | // element conversions in elemConvs | |
242 | return func(val cty.Value, path cty.Path) (cty.Value, error) { | |
243 | elems := make([]cty.Value, 0, len(elemConvs)) | |
244 | path = append(path, nil) | |
245 | i := int64(0) | |
246 | it := val.ElementIterator() | |
247 | for it.Next() { | |
248 | _, val := it.Element() | |
249 | var err error | |
250 | ||
251 | path[len(path)-1] = cty.IndexStep{ | |
252 | Key: cty.NumberIntVal(i), | |
253 | } | |
254 | ||
255 | conv := elemConvs[i] | |
256 | if conv != nil { | |
257 | val, err = conv(val, path) | |
258 | if err != nil { | |
259 | return cty.NilVal, err | |
260 | } | |
261 | } | |
262 | elems = append(elems, val) | |
263 | ||
264 | i++ | |
265 | } | |
266 | ||
267 | return cty.ListVal(elems), nil | |
268 | } | |
269 | } | |
270 | ||
271 | // conversionObjectToMap returns a conversion that will take a value of the | |
272 | // given object type and return a map of the given element type. | |
273 | // | |
274 | // Will panic if the given objectType isn't actually an object type. | |
275 | func conversionObjectToMap(objectType cty.Type, mapEty cty.Type, unsafe bool) conversion { | |
276 | objectAtys := objectType.AttributeTypes() | |
277 | ||
278 | if len(objectAtys) == 0 { | |
279 | // Empty object short-circuit | |
280 | return func(val cty.Value, path cty.Path) (cty.Value, error) { | |
281 | return cty.MapValEmpty(mapEty), nil | |
282 | } | |
283 | } | |
284 | ||
285 | if mapEty == cty.DynamicPseudoType { | |
286 | // This is a special case where the caller wants us to find | |
287 | // a suitable single type that all elements can convert to, if | |
288 | // possible. | |
289 | objectAtysList := make([]cty.Type, 0, len(objectAtys)) | |
290 | for _, aty := range objectAtys { | |
291 | objectAtysList = append(objectAtysList, aty) | |
292 | } | |
293 | mapEty, _ = unify(objectAtysList, unsafe) | |
294 | if mapEty == cty.NilType { | |
295 | return nil | |
296 | } | |
297 | } | |
298 | ||
299 | elemConvs := make(map[string]conversion, len(objectAtys)) | |
300 | for name, objectAty := range objectAtys { | |
301 | if objectAty.Equals(mapEty) { | |
302 | // no conversion required | |
303 | continue | |
304 | } | |
305 | ||
306 | elemConvs[name] = getConversion(objectAty, mapEty, unsafe) | |
307 | if elemConvs[name] == nil { | |
308 | // If any of our element conversions are impossible, then the our | |
309 | // whole conversion is impossible. | |
310 | return nil | |
311 | } | |
312 | } | |
313 | ||
314 | // If we fall out here then a conversion is possible, using the | |
315 | // element conversions in elemConvs | |
316 | return func(val cty.Value, path cty.Path) (cty.Value, error) { | |
317 | elems := make(map[string]cty.Value, len(elemConvs)) | |
318 | path = append(path, nil) | |
319 | it := val.ElementIterator() | |
320 | for it.Next() { | |
321 | name, val := it.Element() | |
322 | var err error | |
323 | ||
324 | path[len(path)-1] = cty.IndexStep{ | |
325 | Key: name, | |
326 | } | |
327 | ||
328 | conv := elemConvs[name.AsString()] | |
329 | if conv != nil { | |
330 | val, err = conv(val, path) | |
331 | if err != nil { | |
332 | return cty.NilVal, err | |
333 | } | |
334 | } | |
335 | elems[name.AsString()] = val | |
336 | } | |
337 | ||
338 | return cty.MapVal(elems), nil | |
339 | } | |
340 | } |