4 "github.com/zclconf/go-cty/cty"
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.
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())
18 path = append(path, nil)
19 it := val.ElementIterator()
21 _, val := it.Element()
24 path[len(path)-1] = cty.IndexStep{
25 Key: cty.NumberIntVal(i),
29 val, err = conv(val, path)
31 return cty.NilVal, err
34 elems = append(elems, val)
40 return cty.ListValEmpty(ety), nil
43 return cty.ListVal(elems), nil
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.
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())
58 path = append(path, nil)
59 it := val.ElementIterator()
61 _, val := it.Element()
64 path[len(path)-1] = cty.IndexStep{
65 Key: cty.NumberIntVal(i),
69 val, err = conv(val, path)
71 return cty.NilVal, err
74 elems = append(elems, val)
80 return cty.SetValEmpty(ety), nil
83 return cty.SetVal(elems), nil
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.
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()
99 key, val := it.Element()
102 path[len(path)-1] = cty.IndexStep{
106 keyStr, err := Convert(key, cty.String)
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())
114 val, err = conv(val, path)
116 return cty.NilVal, err
120 elems[keyStr.AsString()] = val
124 return cty.MapValEmpty(ety), nil
127 return cty.MapVal(elems), nil
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.
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()
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
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
149 listEty, _ = unify(tupleEtys, unsafe)
150 if listEty == cty.NilType {
155 elemConvs := make([]conversion, len(tupleEtys))
156 for i, tupleEty := range tupleEtys {
157 if tupleEty.Equals(listEty) {
158 // no conversion required
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.
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)
176 it := val.ElementIterator()
178 _, val := it.Element()
181 path[len(path)-1] = cty.IndexStep{
182 Key: cty.NumberIntVal(i),
187 val, err = conv(val, path)
189 return cty.NilVal, err
192 elems = append(elems, val)
197 return cty.SetVal(elems), nil
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.
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()
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
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
219 listEty, _ = unify(tupleEtys, unsafe)
220 if listEty == cty.NilType {
225 elemConvs := make([]conversion, len(tupleEtys))
226 for i, tupleEty := range tupleEtys {
227 if tupleEty.Equals(listEty) {
228 // no conversion required
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.
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)
246 it := val.ElementIterator()
248 _, val := it.Element()
251 path[len(path)-1] = cty.IndexStep{
252 Key: cty.NumberIntVal(i),
257 val, err = conv(val, path)
259 return cty.NilVal, err
262 elems = append(elems, val)
267 return cty.ListVal(elems), nil
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.
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()
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
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
289 objectAtysList := make([]cty.Type, 0, len(objectAtys))
290 for _, aty := range objectAtys {
291 objectAtysList = append(objectAtysList, aty)
293 mapEty, _ = unify(objectAtysList, unsafe)
294 if mapEty == cty.NilType {
299 elemConvs := make(map[string]conversion, len(objectAtys))
300 for name, objectAty := range objectAtys {
301 if objectAty.Equals(mapEty) {
302 // no conversion required
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.
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()
321 name, val := it.Element()
324 path[len(path)-1] = cty.IndexStep{
328 conv := elemConvs[name.AsString()]
330 val, err = conv(val, path)
332 return cty.NilVal, err
335 elems[name.AsString()] = val
338 return cty.MapVal(elems), nil