diff options
author | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
---|---|---|
committer | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
commit | 107c1cdb09c575aa2f61d97f48d8587eb6bada4c (patch) | |
tree | ca7d008643efc555c388baeaf1d986e0b6b3e28c /vendor/github.com/hashicorp/terraform/lang/funcs/collection.go | |
parent | 844b5a68d8af4791755b8f0ad293cc99f5959183 (diff) | |
download | terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.gz terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.zst terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.zip |
Upgrade to 0.12
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/lang/funcs/collection.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/lang/funcs/collection.go | 1511 |
1 files changed, 1511 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/collection.go b/vendor/github.com/hashicorp/terraform/lang/funcs/collection.go new file mode 100644 index 0000000..71b7a84 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/lang/funcs/collection.go | |||
@@ -0,0 +1,1511 @@ | |||
1 | package funcs | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | "sort" | ||
7 | |||
8 | "github.com/zclconf/go-cty/cty" | ||
9 | "github.com/zclconf/go-cty/cty/convert" | ||
10 | "github.com/zclconf/go-cty/cty/function" | ||
11 | "github.com/zclconf/go-cty/cty/function/stdlib" | ||
12 | "github.com/zclconf/go-cty/cty/gocty" | ||
13 | ) | ||
14 | |||
15 | var ElementFunc = function.New(&function.Spec{ | ||
16 | Params: []function.Parameter{ | ||
17 | { | ||
18 | Name: "list", | ||
19 | Type: cty.DynamicPseudoType, | ||
20 | }, | ||
21 | { | ||
22 | Name: "index", | ||
23 | Type: cty.Number, | ||
24 | }, | ||
25 | }, | ||
26 | Type: func(args []cty.Value) (cty.Type, error) { | ||
27 | list := args[0] | ||
28 | listTy := list.Type() | ||
29 | switch { | ||
30 | case listTy.IsListType(): | ||
31 | return listTy.ElementType(), nil | ||
32 | case listTy.IsTupleType(): | ||
33 | if !args[1].IsKnown() { | ||
34 | // If the index isn't known yet then we can't predict the | ||
35 | // result type since each tuple element can have its own type. | ||
36 | return cty.DynamicPseudoType, nil | ||
37 | } | ||
38 | |||
39 | etys := listTy.TupleElementTypes() | ||
40 | var index int | ||
41 | err := gocty.FromCtyValue(args[1], &index) | ||
42 | if err != nil { | ||
43 | // e.g. fractional number where whole number is required | ||
44 | return cty.DynamicPseudoType, fmt.Errorf("invalid index: %s", err) | ||
45 | } | ||
46 | if len(etys) == 0 { | ||
47 | return cty.DynamicPseudoType, errors.New("cannot use element function with an empty list") | ||
48 | } | ||
49 | index = index % len(etys) | ||
50 | return etys[index], nil | ||
51 | default: | ||
52 | return cty.DynamicPseudoType, fmt.Errorf("cannot read elements from %s", listTy.FriendlyName()) | ||
53 | } | ||
54 | }, | ||
55 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | ||
56 | var index int | ||
57 | err := gocty.FromCtyValue(args[1], &index) | ||
58 | if err != nil { | ||
59 | // can't happen because we checked this in the Type function above | ||
60 | return cty.DynamicVal, fmt.Errorf("invalid index: %s", err) | ||
61 | } | ||
62 | |||
63 | if !args[0].IsKnown() { | ||
64 | return cty.UnknownVal(retType), nil | ||
65 | } | ||
66 | |||
67 | l := args[0].LengthInt() | ||
68 | if l == 0 { | ||
69 | return cty.DynamicVal, errors.New("cannot use element function with an empty list") | ||
70 | } | ||
71 | index = index % l | ||
72 | |||
73 | // We did all the necessary type checks in the type function above, | ||
74 | // so this is guaranteed not to fail. | ||
75 | return args[0].Index(cty.NumberIntVal(int64(index))), nil | ||
76 | }, | ||
77 | }) | ||
78 | |||
79 | var LengthFunc = function.New(&function.Spec{ | ||
80 | Params: []function.Parameter{ | ||
81 | { | ||
82 | Name: "value", | ||
83 | Type: cty.DynamicPseudoType, | ||
84 | AllowDynamicType: true, | ||
85 | AllowUnknown: true, | ||
86 | }, | ||
87 | }, | ||
88 | Type: func(args []cty.Value) (cty.Type, error) { | ||
89 | collTy := args[0].Type() | ||
90 | switch { | ||
91 | case collTy == cty.String || collTy.IsTupleType() || collTy.IsObjectType() || collTy.IsListType() || collTy.IsMapType() || collTy.IsSetType() || collTy == cty.DynamicPseudoType: | ||
92 | return cty.Number, nil | ||
93 | default: | ||
94 | return cty.Number, errors.New("argument must be a string, a collection type, or a structural type") | ||
95 | } | ||
96 | }, | ||
97 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | ||
98 | coll := args[0] | ||
99 | collTy := args[0].Type() | ||
100 | switch { | ||
101 | case collTy == cty.DynamicPseudoType: | ||
102 | return cty.UnknownVal(cty.Number), nil | ||
103 | case collTy.IsTupleType(): | ||
104 | l := len(collTy.TupleElementTypes()) | ||
105 | return cty.NumberIntVal(int64(l)), nil | ||
106 | case collTy.IsObjectType(): | ||
107 | l := len(collTy.AttributeTypes()) | ||
108 | return cty.NumberIntVal(int64(l)), nil | ||
109 | case collTy == cty.String: | ||
110 | // We'll delegate to the cty stdlib strlen function here, because | ||
111 | // it deals with all of the complexities of tokenizing unicode | ||
112 | // grapheme clusters. | ||
113 | return stdlib.Strlen(coll) | ||
114 | case collTy.IsListType() || collTy.IsSetType() || collTy.IsMapType(): | ||
115 | return coll.Length(), nil | ||
116 | default: | ||
117 | // Should never happen, because of the checks in our Type func above | ||
118 | return cty.UnknownVal(cty.Number), errors.New("impossible value type for length(...)") | ||
119 | } | ||
120 | }, | ||
121 | }) | ||
122 | |||
123 | // CoalesceFunc constructs a function that takes any number of arguments and | ||
124 | // returns the first one that isn't empty. This function was copied from go-cty | ||
125 | // stdlib and modified so that it returns the first *non-empty* non-null element | ||
126 | // from a sequence, instead of merely the first non-null. | ||
127 | var CoalesceFunc = function.New(&function.Spec{ | ||
128 | Params: []function.Parameter{}, | ||
129 | VarParam: &function.Parameter{ | ||
130 | Name: "vals", | ||
131 | Type: cty.DynamicPseudoType, | ||
132 | AllowUnknown: true, | ||
133 | AllowDynamicType: true, | ||
134 | AllowNull: true, | ||
135 | }, | ||
136 | Type: func(args []cty.Value) (ret cty.Type, err error) { | ||
137 | argTypes := make([]cty.Type, len(args)) | ||
138 | for i, val := range args { | ||
139 | argTypes[i] = val.Type() | ||
140 | } | ||
141 | retType, _ := convert.UnifyUnsafe(argTypes) | ||
142 | if retType == cty.NilType { | ||
143 | return cty.NilType, errors.New("all arguments must have the same type") | ||
144 | } | ||
145 | return retType, nil | ||
146 | }, | ||
147 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
148 | for _, argVal := range args { | ||
149 | // We already know this will succeed because of the checks in our Type func above | ||
150 | argVal, _ = convert.Convert(argVal, retType) | ||
151 | if !argVal.IsKnown() { | ||
152 | return cty.UnknownVal(retType), nil | ||
153 | } | ||
154 | if argVal.IsNull() { | ||
155 | continue | ||
156 | } | ||
157 | if retType == cty.String && argVal.RawEquals(cty.StringVal("")) { | ||
158 | continue | ||
159 | } | ||
160 | |||
161 | return argVal, nil | ||
162 | } | ||
163 | return cty.NilVal, errors.New("no non-null, non-empty-string arguments") | ||
164 | }, | ||
165 | }) | ||
166 | |||
167 | // CoalesceListFunc constructs a function that takes any number of list arguments | ||
168 | // and returns the first one that isn't empty. | ||
169 | var CoalesceListFunc = function.New(&function.Spec{ | ||
170 | Params: []function.Parameter{}, | ||
171 | VarParam: &function.Parameter{ | ||
172 | Name: "vals", | ||
173 | Type: cty.DynamicPseudoType, | ||
174 | AllowUnknown: true, | ||
175 | AllowDynamicType: true, | ||
176 | AllowNull: true, | ||
177 | }, | ||
178 | Type: func(args []cty.Value) (ret cty.Type, err error) { | ||
179 | if len(args) == 0 { | ||
180 | return cty.NilType, errors.New("at least one argument is required") | ||
181 | } | ||
182 | |||
183 | argTypes := make([]cty.Type, len(args)) | ||
184 | |||
185 | for i, arg := range args { | ||
186 | // if any argument is unknown, we can't be certain know which type we will return | ||
187 | if !arg.IsKnown() { | ||
188 | return cty.DynamicPseudoType, nil | ||
189 | } | ||
190 | ty := arg.Type() | ||
191 | |||
192 | if !ty.IsListType() && !ty.IsTupleType() { | ||
193 | return cty.NilType, errors.New("coalescelist arguments must be lists or tuples") | ||
194 | } | ||
195 | |||
196 | argTypes[i] = arg.Type() | ||
197 | } | ||
198 | |||
199 | last := argTypes[0] | ||
200 | // If there are mixed types, we have to return a dynamic type. | ||
201 | for _, next := range argTypes[1:] { | ||
202 | if !next.Equals(last) { | ||
203 | return cty.DynamicPseudoType, nil | ||
204 | } | ||
205 | } | ||
206 | |||
207 | return last, nil | ||
208 | }, | ||
209 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
210 | for _, arg := range args { | ||
211 | if !arg.IsKnown() { | ||
212 | // If we run into an unknown list at some point, we can't | ||
213 | // predict the final result yet. (If there's a known, non-empty | ||
214 | // arg before this then we won't get here.) | ||
215 | return cty.UnknownVal(retType), nil | ||
216 | } | ||
217 | |||
218 | if arg.LengthInt() > 0 { | ||
219 | return arg, nil | ||
220 | } | ||
221 | } | ||
222 | |||
223 | return cty.NilVal, errors.New("no non-null arguments") | ||
224 | }, | ||
225 | }) | ||
226 | |||
227 | // CompactFunc constructs a function that takes a list of strings and returns a new list | ||
228 | // with any empty string elements removed. | ||
229 | var CompactFunc = function.New(&function.Spec{ | ||
230 | Params: []function.Parameter{ | ||
231 | { | ||
232 | Name: "list", | ||
233 | Type: cty.List(cty.String), | ||
234 | }, | ||
235 | }, | ||
236 | Type: function.StaticReturnType(cty.List(cty.String)), | ||
237 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
238 | listVal := args[0] | ||
239 | if !listVal.IsWhollyKnown() { | ||
240 | // If some of the element values aren't known yet then we | ||
241 | // can't yet return a compacted list | ||
242 | return cty.UnknownVal(retType), nil | ||
243 | } | ||
244 | |||
245 | var outputList []cty.Value | ||
246 | |||
247 | for it := listVal.ElementIterator(); it.Next(); { | ||
248 | _, v := it.Element() | ||
249 | if v.AsString() == "" { | ||
250 | continue | ||
251 | } | ||
252 | outputList = append(outputList, v) | ||
253 | } | ||
254 | |||
255 | if len(outputList) == 0 { | ||
256 | return cty.ListValEmpty(cty.String), nil | ||
257 | } | ||
258 | |||
259 | return cty.ListVal(outputList), nil | ||
260 | }, | ||
261 | }) | ||
262 | |||
263 | // ContainsFunc constructs a function that determines whether a given list or | ||
264 | // set contains a given single value as one of its elements. | ||
265 | var ContainsFunc = function.New(&function.Spec{ | ||
266 | Params: []function.Parameter{ | ||
267 | { | ||
268 | Name: "list", | ||
269 | Type: cty.DynamicPseudoType, | ||
270 | }, | ||
271 | { | ||
272 | Name: "value", | ||
273 | Type: cty.DynamicPseudoType, | ||
274 | }, | ||
275 | }, | ||
276 | Type: function.StaticReturnType(cty.Bool), | ||
277 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
278 | arg := args[0] | ||
279 | ty := arg.Type() | ||
280 | |||
281 | if !ty.IsListType() && !ty.IsTupleType() && !ty.IsSetType() { | ||
282 | return cty.NilVal, errors.New("argument must be list, tuple, or set") | ||
283 | } | ||
284 | |||
285 | _, err = Index(cty.TupleVal(arg.AsValueSlice()), args[1]) | ||
286 | if err != nil { | ||
287 | return cty.False, nil | ||
288 | } | ||
289 | |||
290 | return cty.True, nil | ||
291 | }, | ||
292 | }) | ||
293 | |||
294 | // IndexFunc constructs a function that finds the element index for a given value in a list. | ||
295 | var IndexFunc = function.New(&function.Spec{ | ||
296 | Params: []function.Parameter{ | ||
297 | { | ||
298 | Name: "list", | ||
299 | Type: cty.DynamicPseudoType, | ||
300 | }, | ||
301 | { | ||
302 | Name: "value", | ||
303 | Type: cty.DynamicPseudoType, | ||
304 | }, | ||
305 | }, | ||
306 | Type: function.StaticReturnType(cty.Number), | ||
307 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
308 | if !(args[0].Type().IsListType() || args[0].Type().IsTupleType()) { | ||
309 | return cty.NilVal, errors.New("argument must be a list or tuple") | ||
310 | } | ||
311 | |||
312 | if !args[0].IsKnown() { | ||
313 | return cty.UnknownVal(cty.Number), nil | ||
314 | } | ||
315 | |||
316 | if args[0].LengthInt() == 0 { // Easy path | ||
317 | return cty.NilVal, errors.New("cannot search an empty list") | ||
318 | } | ||
319 | |||
320 | for it := args[0].ElementIterator(); it.Next(); { | ||
321 | i, v := it.Element() | ||
322 | eq, err := stdlib.Equal(v, args[1]) | ||
323 | if err != nil { | ||
324 | return cty.NilVal, err | ||
325 | } | ||
326 | if !eq.IsKnown() { | ||
327 | return cty.UnknownVal(cty.Number), nil | ||
328 | } | ||
329 | if eq.True() { | ||
330 | return i, nil | ||
331 | } | ||
332 | } | ||
333 | return cty.NilVal, errors.New("item not found") | ||
334 | |||
335 | }, | ||
336 | }) | ||
337 | |||
338 | // DistinctFunc constructs a function that takes a list and returns a new list | ||
339 | // with any duplicate elements removed. | ||
340 | var DistinctFunc = function.New(&function.Spec{ | ||
341 | Params: []function.Parameter{ | ||
342 | { | ||
343 | Name: "list", | ||
344 | Type: cty.List(cty.DynamicPseudoType), | ||
345 | }, | ||
346 | }, | ||
347 | Type: func(args []cty.Value) (cty.Type, error) { | ||
348 | return args[0].Type(), nil | ||
349 | }, | ||
350 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
351 | listVal := args[0] | ||
352 | |||
353 | if !listVal.IsWhollyKnown() { | ||
354 | return cty.UnknownVal(retType), nil | ||
355 | } | ||
356 | var list []cty.Value | ||
357 | |||
358 | for it := listVal.ElementIterator(); it.Next(); { | ||
359 | _, v := it.Element() | ||
360 | list, err = appendIfMissing(list, v) | ||
361 | if err != nil { | ||
362 | return cty.NilVal, err | ||
363 | } | ||
364 | } | ||
365 | |||
366 | return cty.ListVal(list), nil | ||
367 | }, | ||
368 | }) | ||
369 | |||
370 | // ChunklistFunc constructs a function that splits a single list into fixed-size chunks, | ||
371 | // returning a list of lists. | ||
372 | var ChunklistFunc = function.New(&function.Spec{ | ||
373 | Params: []function.Parameter{ | ||
374 | { | ||
375 | Name: "list", | ||
376 | Type: cty.List(cty.DynamicPseudoType), | ||
377 | }, | ||
378 | { | ||
379 | Name: "size", | ||
380 | Type: cty.Number, | ||
381 | }, | ||
382 | }, | ||
383 | Type: func(args []cty.Value) (cty.Type, error) { | ||
384 | return cty.List(args[0].Type()), nil | ||
385 | }, | ||
386 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
387 | listVal := args[0] | ||
388 | if !listVal.IsKnown() { | ||
389 | return cty.UnknownVal(retType), nil | ||
390 | } | ||
391 | |||
392 | var size int | ||
393 | err = gocty.FromCtyValue(args[1], &size) | ||
394 | if err != nil { | ||
395 | return cty.NilVal, fmt.Errorf("invalid index: %s", err) | ||
396 | } | ||
397 | |||
398 | if size < 0 { | ||
399 | return cty.NilVal, errors.New("the size argument must be positive") | ||
400 | } | ||
401 | |||
402 | output := make([]cty.Value, 0) | ||
403 | |||
404 | // if size is 0, returns a list made of the initial list | ||
405 | if size == 0 { | ||
406 | output = append(output, listVal) | ||
407 | return cty.ListVal(output), nil | ||
408 | } | ||
409 | |||
410 | chunk := make([]cty.Value, 0) | ||
411 | |||
412 | l := args[0].LengthInt() | ||
413 | i := 0 | ||
414 | |||
415 | for it := listVal.ElementIterator(); it.Next(); { | ||
416 | _, v := it.Element() | ||
417 | chunk = append(chunk, v) | ||
418 | |||
419 | // Chunk when index isn't 0, or when reaching the values's length | ||
420 | if (i+1)%size == 0 || (i+1) == l { | ||
421 | output = append(output, cty.ListVal(chunk)) | ||
422 | chunk = make([]cty.Value, 0) | ||
423 | } | ||
424 | i++ | ||
425 | } | ||
426 | |||
427 | return cty.ListVal(output), nil | ||
428 | }, | ||
429 | }) | ||
430 | |||
431 | // FlattenFunc constructs a function that takes a list and replaces any elements | ||
432 | // that are lists with a flattened sequence of the list contents. | ||
433 | var FlattenFunc = function.New(&function.Spec{ | ||
434 | Params: []function.Parameter{ | ||
435 | { | ||
436 | Name: "list", | ||
437 | Type: cty.DynamicPseudoType, | ||
438 | }, | ||
439 | }, | ||
440 | Type: func(args []cty.Value) (cty.Type, error) { | ||
441 | if !args[0].IsWhollyKnown() { | ||
442 | return cty.DynamicPseudoType, nil | ||
443 | } | ||
444 | |||
445 | argTy := args[0].Type() | ||
446 | if !argTy.IsListType() && !argTy.IsSetType() && !argTy.IsTupleType() { | ||
447 | return cty.NilType, errors.New("can only flatten lists, sets and tuples") | ||
448 | } | ||
449 | |||
450 | retVal, known := flattener(args[0]) | ||
451 | if !known { | ||
452 | return cty.DynamicPseudoType, nil | ||
453 | } | ||
454 | |||
455 | tys := make([]cty.Type, len(retVal)) | ||
456 | for i, ty := range retVal { | ||
457 | tys[i] = ty.Type() | ||
458 | } | ||
459 | return cty.Tuple(tys), nil | ||
460 | }, | ||
461 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
462 | inputList := args[0] | ||
463 | if inputList.LengthInt() == 0 { | ||
464 | return cty.EmptyTupleVal, nil | ||
465 | } | ||
466 | |||
467 | out, known := flattener(inputList) | ||
468 | if !known { | ||
469 | return cty.UnknownVal(retType), nil | ||
470 | } | ||
471 | |||
472 | return cty.TupleVal(out), nil | ||
473 | }, | ||
474 | }) | ||
475 | |||
476 | // Flatten until it's not a cty.List, and return whether the value is known. | ||
477 | // We can flatten lists with unknown values, as long as they are not | ||
478 | // lists themselves. | ||
479 | func flattener(flattenList cty.Value) ([]cty.Value, bool) { | ||
480 | out := make([]cty.Value, 0) | ||
481 | for it := flattenList.ElementIterator(); it.Next(); { | ||
482 | _, val := it.Element() | ||
483 | if val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType() { | ||
484 | if !val.IsKnown() { | ||
485 | return out, false | ||
486 | } | ||
487 | |||
488 | res, known := flattener(val) | ||
489 | if !known { | ||
490 | return res, known | ||
491 | } | ||
492 | out = append(out, res...) | ||
493 | } else { | ||
494 | out = append(out, val) | ||
495 | } | ||
496 | } | ||
497 | return out, true | ||
498 | } | ||
499 | |||
500 | // KeysFunc constructs a function that takes a map and returns a sorted list of the map keys. | ||
501 | var KeysFunc = function.New(&function.Spec{ | ||
502 | Params: []function.Parameter{ | ||
503 | { | ||
504 | Name: "inputMap", | ||
505 | Type: cty.DynamicPseudoType, | ||
506 | AllowUnknown: true, | ||
507 | }, | ||
508 | }, | ||
509 | Type: func(args []cty.Value) (cty.Type, error) { | ||
510 | ty := args[0].Type() | ||
511 | switch { | ||
512 | case ty.IsMapType(): | ||
513 | return cty.List(cty.String), nil | ||
514 | case ty.IsObjectType(): | ||
515 | atys := ty.AttributeTypes() | ||
516 | if len(atys) == 0 { | ||
517 | return cty.EmptyTuple, nil | ||
518 | } | ||
519 | // All of our result elements will be strings, and atys just | ||
520 | // decides how many there are. | ||
521 | etys := make([]cty.Type, len(atys)) | ||
522 | for i := range etys { | ||
523 | etys[i] = cty.String | ||
524 | } | ||
525 | return cty.Tuple(etys), nil | ||
526 | default: | ||
527 | return cty.DynamicPseudoType, function.NewArgErrorf(0, "must have map or object type") | ||
528 | } | ||
529 | }, | ||
530 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | ||
531 | m := args[0] | ||
532 | var keys []cty.Value | ||
533 | |||
534 | switch { | ||
535 | case m.Type().IsObjectType(): | ||
536 | // In this case we allow unknown values so we must work only with | ||
537 | // the attribute _types_, not with the value itself. | ||
538 | var names []string | ||
539 | for name := range m.Type().AttributeTypes() { | ||
540 | names = append(names, name) | ||
541 | } | ||
542 | sort.Strings(names) // same ordering guaranteed by cty's ElementIterator | ||
543 | if len(names) == 0 { | ||
544 | return cty.EmptyTupleVal, nil | ||
545 | } | ||
546 | keys = make([]cty.Value, len(names)) | ||
547 | for i, name := range names { | ||
548 | keys[i] = cty.StringVal(name) | ||
549 | } | ||
550 | return cty.TupleVal(keys), nil | ||
551 | default: | ||
552 | if !m.IsKnown() { | ||
553 | return cty.UnknownVal(retType), nil | ||
554 | } | ||
555 | |||
556 | // cty guarantees that ElementIterator will iterate in lexicographical | ||
557 | // order by key. | ||
558 | for it := args[0].ElementIterator(); it.Next(); { | ||
559 | k, _ := it.Element() | ||
560 | keys = append(keys, k) | ||
561 | } | ||
562 | if len(keys) == 0 { | ||
563 | return cty.ListValEmpty(cty.String), nil | ||
564 | } | ||
565 | return cty.ListVal(keys), nil | ||
566 | } | ||
567 | }, | ||
568 | }) | ||
569 | |||
570 | // ListFunc constructs a function that takes an arbitrary number of arguments | ||
571 | // and returns a list containing those values in the same order. | ||
572 | // | ||
573 | // This function is deprecated in Terraform v0.12 | ||
574 | var ListFunc = function.New(&function.Spec{ | ||
575 | Params: []function.Parameter{}, | ||
576 | VarParam: &function.Parameter{ | ||
577 | Name: "vals", | ||
578 | Type: cty.DynamicPseudoType, | ||
579 | AllowUnknown: true, | ||
580 | AllowDynamicType: true, | ||
581 | AllowNull: true, | ||
582 | }, | ||
583 | Type: func(args []cty.Value) (ret cty.Type, err error) { | ||
584 | if len(args) == 0 { | ||
585 | return cty.NilType, errors.New("at least one argument is required") | ||
586 | } | ||
587 | |||
588 | argTypes := make([]cty.Type, len(args)) | ||
589 | |||
590 | for i, arg := range args { | ||
591 | argTypes[i] = arg.Type() | ||
592 | } | ||
593 | |||
594 | retType, _ := convert.UnifyUnsafe(argTypes) | ||
595 | if retType == cty.NilType { | ||
596 | return cty.NilType, errors.New("all arguments must have the same type") | ||
597 | } | ||
598 | |||
599 | return cty.List(retType), nil | ||
600 | }, | ||
601 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
602 | newList := make([]cty.Value, 0, len(args)) | ||
603 | |||
604 | for _, arg := range args { | ||
605 | // We already know this will succeed because of the checks in our Type func above | ||
606 | arg, _ = convert.Convert(arg, retType.ElementType()) | ||
607 | newList = append(newList, arg) | ||
608 | } | ||
609 | |||
610 | return cty.ListVal(newList), nil | ||
611 | }, | ||
612 | }) | ||
613 | |||
614 | // LookupFunc constructs a function that performs dynamic lookups of map types. | ||
615 | var LookupFunc = function.New(&function.Spec{ | ||
616 | Params: []function.Parameter{ | ||
617 | { | ||
618 | Name: "inputMap", | ||
619 | Type: cty.DynamicPseudoType, | ||
620 | }, | ||
621 | { | ||
622 | Name: "key", | ||
623 | Type: cty.String, | ||
624 | }, | ||
625 | }, | ||
626 | VarParam: &function.Parameter{ | ||
627 | Name: "default", | ||
628 | Type: cty.DynamicPseudoType, | ||
629 | AllowUnknown: true, | ||
630 | AllowDynamicType: true, | ||
631 | AllowNull: true, | ||
632 | }, | ||
633 | Type: func(args []cty.Value) (ret cty.Type, err error) { | ||
634 | if len(args) < 1 || len(args) > 3 { | ||
635 | return cty.NilType, fmt.Errorf("lookup() takes two or three arguments, got %d", len(args)) | ||
636 | } | ||
637 | |||
638 | ty := args[0].Type() | ||
639 | |||
640 | switch { | ||
641 | case ty.IsObjectType(): | ||
642 | if !args[1].IsKnown() { | ||
643 | return cty.DynamicPseudoType, nil | ||
644 | } | ||
645 | |||
646 | key := args[1].AsString() | ||
647 | if ty.HasAttribute(key) { | ||
648 | return args[0].GetAttr(key).Type(), nil | ||
649 | } else if len(args) == 3 { | ||
650 | // if the key isn't found but a default is provided, | ||
651 | // return the default type | ||
652 | return args[2].Type(), nil | ||
653 | } | ||
654 | return cty.DynamicPseudoType, function.NewArgErrorf(0, "the given object has no attribute %q", key) | ||
655 | case ty.IsMapType(): | ||
656 | return ty.ElementType(), nil | ||
657 | default: | ||
658 | return cty.NilType, function.NewArgErrorf(0, "lookup() requires a map as the first argument") | ||
659 | } | ||
660 | }, | ||
661 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
662 | var defaultVal cty.Value | ||
663 | defaultValueSet := false | ||
664 | |||
665 | if len(args) == 3 { | ||
666 | defaultVal = args[2] | ||
667 | defaultValueSet = true | ||
668 | } | ||
669 | |||
670 | mapVar := args[0] | ||
671 | lookupKey := args[1].AsString() | ||
672 | |||
673 | if !mapVar.IsWhollyKnown() { | ||
674 | return cty.UnknownVal(retType), nil | ||
675 | } | ||
676 | |||
677 | if mapVar.Type().IsObjectType() { | ||
678 | if mapVar.Type().HasAttribute(lookupKey) { | ||
679 | return mapVar.GetAttr(lookupKey), nil | ||
680 | } | ||
681 | } else if mapVar.HasIndex(cty.StringVal(lookupKey)) == cty.True { | ||
682 | v := mapVar.Index(cty.StringVal(lookupKey)) | ||
683 | if ty := v.Type(); !ty.Equals(cty.NilType) { | ||
684 | switch { | ||
685 | case ty.Equals(cty.String): | ||
686 | return cty.StringVal(v.AsString()), nil | ||
687 | case ty.Equals(cty.Number): | ||
688 | return cty.NumberVal(v.AsBigFloat()), nil | ||
689 | default: | ||
690 | return cty.NilVal, errors.New("lookup() can only be used with flat lists") | ||
691 | } | ||
692 | } | ||
693 | } | ||
694 | |||
695 | if defaultValueSet { | ||
696 | defaultVal, err = convert.Convert(defaultVal, retType) | ||
697 | if err != nil { | ||
698 | return cty.NilVal, err | ||
699 | } | ||
700 | return defaultVal, nil | ||
701 | } | ||
702 | |||
703 | return cty.UnknownVal(cty.DynamicPseudoType), fmt.Errorf( | ||
704 | "lookup failed to find '%s'", lookupKey) | ||
705 | }, | ||
706 | }) | ||
707 | |||
708 | // MapFunc constructs a function that takes an even number of arguments and | ||
709 | // returns a map whose elements are constructed from consecutive pairs of arguments. | ||
710 | // | ||
711 | // This function is deprecated in Terraform v0.12 | ||
712 | var MapFunc = function.New(&function.Spec{ | ||
713 | Params: []function.Parameter{}, | ||
714 | VarParam: &function.Parameter{ | ||
715 | Name: "vals", | ||
716 | Type: cty.DynamicPseudoType, | ||
717 | AllowUnknown: true, | ||
718 | AllowDynamicType: true, | ||
719 | AllowNull: true, | ||
720 | }, | ||
721 | Type: func(args []cty.Value) (ret cty.Type, err error) { | ||
722 | if len(args) < 2 || len(args)%2 != 0 { | ||
723 | return cty.NilType, fmt.Errorf("map requires an even number of two or more arguments, got %d", len(args)) | ||
724 | } | ||
725 | |||
726 | argTypes := make([]cty.Type, len(args)/2) | ||
727 | index := 0 | ||
728 | |||
729 | for i := 0; i < len(args); i += 2 { | ||
730 | argTypes[index] = args[i+1].Type() | ||
731 | index++ | ||
732 | } | ||
733 | |||
734 | valType, _ := convert.UnifyUnsafe(argTypes) | ||
735 | if valType == cty.NilType { | ||
736 | return cty.NilType, errors.New("all arguments must have the same type") | ||
737 | } | ||
738 | |||
739 | return cty.Map(valType), nil | ||
740 | }, | ||
741 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
742 | for _, arg := range args { | ||
743 | if !arg.IsWhollyKnown() { | ||
744 | return cty.UnknownVal(retType), nil | ||
745 | } | ||
746 | } | ||
747 | |||
748 | outputMap := make(map[string]cty.Value) | ||
749 | |||
750 | for i := 0; i < len(args); i += 2 { | ||
751 | |||
752 | key := args[i].AsString() | ||
753 | |||
754 | err := gocty.FromCtyValue(args[i], &key) | ||
755 | if err != nil { | ||
756 | return cty.NilVal, err | ||
757 | } | ||
758 | |||
759 | val := args[i+1] | ||
760 | |||
761 | var variable cty.Value | ||
762 | err = gocty.FromCtyValue(val, &variable) | ||
763 | if err != nil { | ||
764 | return cty.NilVal, err | ||
765 | } | ||
766 | |||
767 | // We already know this will succeed because of the checks in our Type func above | ||
768 | variable, _ = convert.Convert(variable, retType.ElementType()) | ||
769 | |||
770 | // Check for duplicate keys | ||
771 | if _, ok := outputMap[key]; ok { | ||
772 | return cty.NilVal, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key) | ||
773 | } | ||
774 | outputMap[key] = variable | ||
775 | } | ||
776 | |||
777 | return cty.MapVal(outputMap), nil | ||
778 | }, | ||
779 | }) | ||
780 | |||
781 | // MatchkeysFunc constructs a function that constructs a new list by taking a | ||
782 | // subset of elements from one list whose indexes match the corresponding | ||
783 | // indexes of values in another list. | ||
784 | var MatchkeysFunc = function.New(&function.Spec{ | ||
785 | Params: []function.Parameter{ | ||
786 | { | ||
787 | Name: "values", | ||
788 | Type: cty.List(cty.DynamicPseudoType), | ||
789 | }, | ||
790 | { | ||
791 | Name: "keys", | ||
792 | Type: cty.List(cty.DynamicPseudoType), | ||
793 | }, | ||
794 | { | ||
795 | Name: "searchset", | ||
796 | Type: cty.List(cty.DynamicPseudoType), | ||
797 | }, | ||
798 | }, | ||
799 | Type: func(args []cty.Value) (cty.Type, error) { | ||
800 | if !args[1].Type().Equals(args[2].Type()) { | ||
801 | return cty.NilType, errors.New("lists must be of the same type") | ||
802 | } | ||
803 | |||
804 | return args[0].Type(), nil | ||
805 | }, | ||
806 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
807 | if !args[0].IsKnown() { | ||
808 | return cty.UnknownVal(cty.List(retType.ElementType())), nil | ||
809 | } | ||
810 | |||
811 | if args[0].LengthInt() != args[1].LengthInt() { | ||
812 | return cty.ListValEmpty(retType.ElementType()), errors.New("length of keys and values should be equal") | ||
813 | } | ||
814 | |||
815 | output := make([]cty.Value, 0) | ||
816 | |||
817 | values := args[0] | ||
818 | keys := args[1] | ||
819 | searchset := args[2] | ||
820 | |||
821 | // if searchset is empty, return an empty list. | ||
822 | if searchset.LengthInt() == 0 { | ||
823 | return cty.ListValEmpty(retType.ElementType()), nil | ||
824 | } | ||
825 | |||
826 | if !values.IsWhollyKnown() || !keys.IsWhollyKnown() { | ||
827 | return cty.UnknownVal(retType), nil | ||
828 | } | ||
829 | |||
830 | i := 0 | ||
831 | for it := keys.ElementIterator(); it.Next(); { | ||
832 | _, key := it.Element() | ||
833 | for iter := searchset.ElementIterator(); iter.Next(); { | ||
834 | _, search := iter.Element() | ||
835 | eq, err := stdlib.Equal(key, search) | ||
836 | if err != nil { | ||
837 | return cty.NilVal, err | ||
838 | } | ||
839 | if !eq.IsKnown() { | ||
840 | return cty.ListValEmpty(retType.ElementType()), nil | ||
841 | } | ||
842 | if eq.True() { | ||
843 | v := values.Index(cty.NumberIntVal(int64(i))) | ||
844 | output = append(output, v) | ||
845 | break | ||
846 | } | ||
847 | } | ||
848 | i++ | ||
849 | } | ||
850 | |||
851 | // if we haven't matched any key, then output is an empty list. | ||
852 | if len(output) == 0 { | ||
853 | return cty.ListValEmpty(retType.ElementType()), nil | ||
854 | } | ||
855 | return cty.ListVal(output), nil | ||
856 | }, | ||
857 | }) | ||
858 | |||
859 | // MergeFunc constructs a function that takes an arbitrary number of maps and | ||
860 | // returns a single map that contains a merged set of elements from all of the maps. | ||
861 | // | ||
862 | // If more than one given map defines the same key then the one that is later in | ||
863 | // the argument sequence takes precedence. | ||
864 | var MergeFunc = function.New(&function.Spec{ | ||
865 | Params: []function.Parameter{}, | ||
866 | VarParam: &function.Parameter{ | ||
867 | Name: "maps", | ||
868 | Type: cty.DynamicPseudoType, | ||
869 | AllowDynamicType: true, | ||
870 | AllowNull: true, | ||
871 | }, | ||
872 | Type: function.StaticReturnType(cty.DynamicPseudoType), | ||
873 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
874 | outputMap := make(map[string]cty.Value) | ||
875 | |||
876 | for _, arg := range args { | ||
877 | if !arg.IsWhollyKnown() { | ||
878 | return cty.UnknownVal(retType), nil | ||
879 | } | ||
880 | if !arg.Type().IsObjectType() && !arg.Type().IsMapType() { | ||
881 | return cty.NilVal, fmt.Errorf("arguments must be maps or objects, got %#v", arg.Type().FriendlyName()) | ||
882 | } | ||
883 | for it := arg.ElementIterator(); it.Next(); { | ||
884 | k, v := it.Element() | ||
885 | outputMap[k.AsString()] = v | ||
886 | } | ||
887 | } | ||
888 | return cty.ObjectVal(outputMap), nil | ||
889 | }, | ||
890 | }) | ||
891 | |||
892 | // ReverseFunc takes a sequence and produces a new sequence of the same length | ||
893 | // with all of the same elements as the given sequence but in reverse order. | ||
894 | var ReverseFunc = function.New(&function.Spec{ | ||
895 | Params: []function.Parameter{ | ||
896 | { | ||
897 | Name: "list", | ||
898 | Type: cty.DynamicPseudoType, | ||
899 | }, | ||
900 | }, | ||
901 | Type: func(args []cty.Value) (cty.Type, error) { | ||
902 | argTy := args[0].Type() | ||
903 | switch { | ||
904 | case argTy.IsTupleType(): | ||
905 | argTys := argTy.TupleElementTypes() | ||
906 | retTys := make([]cty.Type, len(argTys)) | ||
907 | for i, ty := range argTys { | ||
908 | retTys[len(retTys)-i-1] = ty | ||
909 | } | ||
910 | return cty.Tuple(retTys), nil | ||
911 | case argTy.IsListType(), argTy.IsSetType(): // We accept sets here to mimic the usual behavior of auto-converting to list | ||
912 | return cty.List(argTy.ElementType()), nil | ||
913 | default: | ||
914 | return cty.NilType, function.NewArgErrorf(0, "can only reverse list or tuple values, not %s", argTy.FriendlyName()) | ||
915 | } | ||
916 | }, | ||
917 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
918 | in := args[0].AsValueSlice() | ||
919 | outVals := make([]cty.Value, len(in)) | ||
920 | for i, v := range in { | ||
921 | outVals[len(outVals)-i-1] = v | ||
922 | } | ||
923 | switch { | ||
924 | case retType.IsTupleType(): | ||
925 | return cty.TupleVal(outVals), nil | ||
926 | default: | ||
927 | if len(outVals) == 0 { | ||
928 | return cty.ListValEmpty(retType.ElementType()), nil | ||
929 | } | ||
930 | return cty.ListVal(outVals), nil | ||
931 | } | ||
932 | }, | ||
933 | }) | ||
934 | |||
935 | // SetProductFunc calculates the cartesian product of two or more sets or | ||
936 | // sequences. If the arguments are all lists then the result is a list of tuples, | ||
937 | // preserving the ordering of all of the input lists. Otherwise the result is a | ||
938 | // set of tuples. | ||
939 | var SetProductFunc = function.New(&function.Spec{ | ||
940 | Params: []function.Parameter{}, | ||
941 | VarParam: &function.Parameter{ | ||
942 | Name: "sets", | ||
943 | Type: cty.DynamicPseudoType, | ||
944 | }, | ||
945 | Type: func(args []cty.Value) (retType cty.Type, err error) { | ||
946 | if len(args) < 2 { | ||
947 | return cty.NilType, errors.New("at least two arguments are required") | ||
948 | } | ||
949 | |||
950 | listCount := 0 | ||
951 | elemTys := make([]cty.Type, len(args)) | ||
952 | for i, arg := range args { | ||
953 | aty := arg.Type() | ||
954 | switch { | ||
955 | case aty.IsSetType(): | ||
956 | elemTys[i] = aty.ElementType() | ||
957 | case aty.IsListType(): | ||
958 | elemTys[i] = aty.ElementType() | ||
959 | listCount++ | ||
960 | case aty.IsTupleType(): | ||
961 | // We can accept a tuple type only if there's some common type | ||
962 | // that all of its elements can be converted to. | ||
963 | allEtys := aty.TupleElementTypes() | ||
964 | if len(allEtys) == 0 { | ||
965 | elemTys[i] = cty.DynamicPseudoType | ||
966 | listCount++ | ||
967 | break | ||
968 | } | ||
969 | ety, _ := convert.UnifyUnsafe(allEtys) | ||
970 | if ety == cty.NilType { | ||
971 | return cty.NilType, function.NewArgErrorf(i, "all elements must be of the same type") | ||
972 | } | ||
973 | elemTys[i] = ety | ||
974 | listCount++ | ||
975 | default: | ||
976 | return cty.NilType, function.NewArgErrorf(i, "a set or a list is required") | ||
977 | } | ||
978 | } | ||
979 | |||
980 | if listCount == len(args) { | ||
981 | return cty.List(cty.Tuple(elemTys)), nil | ||
982 | } | ||
983 | return cty.Set(cty.Tuple(elemTys)), nil | ||
984 | }, | ||
985 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
986 | ety := retType.ElementType() | ||
987 | |||
988 | total := 1 | ||
989 | for _, arg := range args { | ||
990 | // Because of our type checking function, we are guaranteed that | ||
991 | // all of the arguments are known, non-null values of types that | ||
992 | // support LengthInt. | ||
993 | total *= arg.LengthInt() | ||
994 | } | ||
995 | |||
996 | if total == 0 { | ||
997 | // If any of the arguments was an empty collection then our result | ||
998 | // is also an empty collection, which we'll short-circuit here. | ||
999 | if retType.IsListType() { | ||
1000 | return cty.ListValEmpty(ety), nil | ||
1001 | } | ||
1002 | return cty.SetValEmpty(ety), nil | ||
1003 | } | ||
1004 | |||
1005 | subEtys := ety.TupleElementTypes() | ||
1006 | product := make([][]cty.Value, total) | ||
1007 | |||
1008 | b := make([]cty.Value, total*len(args)) | ||
1009 | n := make([]int, len(args)) | ||
1010 | s := 0 | ||
1011 | argVals := make([][]cty.Value, len(args)) | ||
1012 | for i, arg := range args { | ||
1013 | argVals[i] = arg.AsValueSlice() | ||
1014 | } | ||
1015 | |||
1016 | for i := range product { | ||
1017 | e := s + len(args) | ||
1018 | pi := b[s:e] | ||
1019 | product[i] = pi | ||
1020 | s = e | ||
1021 | |||
1022 | for j, n := range n { | ||
1023 | val := argVals[j][n] | ||
1024 | ty := subEtys[j] | ||
1025 | if !val.Type().Equals(ty) { | ||
1026 | var err error | ||
1027 | val, err = convert.Convert(val, ty) | ||
1028 | if err != nil { | ||
1029 | // Should never happen since we checked this in our | ||
1030 | // type-checking function. | ||
1031 | return cty.NilVal, fmt.Errorf("failed to convert argVals[%d][%d] to %s; this is a bug in Terraform", j, n, ty.FriendlyName()) | ||
1032 | } | ||
1033 | } | ||
1034 | pi[j] = val | ||
1035 | } | ||
1036 | |||
1037 | for j := len(n) - 1; j >= 0; j-- { | ||
1038 | n[j]++ | ||
1039 | if n[j] < len(argVals[j]) { | ||
1040 | break | ||
1041 | } | ||
1042 | n[j] = 0 | ||
1043 | } | ||
1044 | } | ||
1045 | |||
1046 | productVals := make([]cty.Value, total) | ||
1047 | for i, vals := range product { | ||
1048 | productVals[i] = cty.TupleVal(vals) | ||
1049 | } | ||
1050 | |||
1051 | if retType.IsListType() { | ||
1052 | return cty.ListVal(productVals), nil | ||
1053 | } | ||
1054 | return cty.SetVal(productVals), nil | ||
1055 | }, | ||
1056 | }) | ||
1057 | |||
1058 | // SliceFunc constructs a function that extracts some consecutive elements | ||
1059 | // from within a list. | ||
1060 | var SliceFunc = function.New(&function.Spec{ | ||
1061 | Params: []function.Parameter{ | ||
1062 | { | ||
1063 | Name: "list", | ||
1064 | Type: cty.DynamicPseudoType, | ||
1065 | }, | ||
1066 | { | ||
1067 | Name: "start_index", | ||
1068 | Type: cty.Number, | ||
1069 | }, | ||
1070 | { | ||
1071 | Name: "end_index", | ||
1072 | Type: cty.Number, | ||
1073 | }, | ||
1074 | }, | ||
1075 | Type: func(args []cty.Value) (cty.Type, error) { | ||
1076 | arg := args[0] | ||
1077 | argTy := arg.Type() | ||
1078 | |||
1079 | if argTy.IsSetType() { | ||
1080 | return cty.NilType, function.NewArgErrorf(0, "cannot slice a set, because its elements do not have indices; use the tolist function to force conversion to list if the ordering of the result is not important") | ||
1081 | } | ||
1082 | if !argTy.IsListType() && !argTy.IsTupleType() { | ||
1083 | return cty.NilType, function.NewArgErrorf(0, "must be a list or tuple value") | ||
1084 | } | ||
1085 | |||
1086 | startIndex, endIndex, idxsKnown, err := sliceIndexes(args) | ||
1087 | if err != nil { | ||
1088 | return cty.NilType, err | ||
1089 | } | ||
1090 | |||
1091 | if argTy.IsListType() { | ||
1092 | return argTy, nil | ||
1093 | } | ||
1094 | |||
1095 | if !idxsKnown { | ||
1096 | // If we don't know our start/end indices then we can't predict | ||
1097 | // the result type if we're planning to return a tuple. | ||
1098 | return cty.DynamicPseudoType, nil | ||
1099 | } | ||
1100 | return cty.Tuple(argTy.TupleElementTypes()[startIndex:endIndex]), nil | ||
1101 | }, | ||
1102 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
1103 | inputList := args[0] | ||
1104 | |||
1105 | if retType == cty.DynamicPseudoType { | ||
1106 | return cty.DynamicVal, nil | ||
1107 | } | ||
1108 | |||
1109 | // we ignore idxsKnown return value here because the indices are always | ||
1110 | // known here, or else the call would've short-circuited. | ||
1111 | startIndex, endIndex, _, err := sliceIndexes(args) | ||
1112 | if err != nil { | ||
1113 | return cty.NilVal, err | ||
1114 | } | ||
1115 | |||
1116 | if endIndex-startIndex == 0 { | ||
1117 | if retType.IsTupleType() { | ||
1118 | return cty.EmptyTupleVal, nil | ||
1119 | } | ||
1120 | return cty.ListValEmpty(retType.ElementType()), nil | ||
1121 | } | ||
1122 | |||
1123 | outputList := inputList.AsValueSlice()[startIndex:endIndex] | ||
1124 | |||
1125 | if retType.IsTupleType() { | ||
1126 | return cty.TupleVal(outputList), nil | ||
1127 | } | ||
1128 | |||
1129 | return cty.ListVal(outputList), nil | ||
1130 | }, | ||
1131 | }) | ||
1132 | |||
1133 | func sliceIndexes(args []cty.Value) (int, int, bool, error) { | ||
1134 | var startIndex, endIndex, length int | ||
1135 | var startKnown, endKnown, lengthKnown bool | ||
1136 | |||
1137 | if args[0].Type().IsTupleType() || args[0].IsKnown() { // if it's a tuple then we always know the length by the type, but lists must be known | ||
1138 | length = args[0].LengthInt() | ||
1139 | lengthKnown = true | ||
1140 | } | ||
1141 | |||
1142 | if args[1].IsKnown() { | ||
1143 | if err := gocty.FromCtyValue(args[1], &startIndex); err != nil { | ||
1144 | return 0, 0, false, function.NewArgErrorf(1, "invalid start index: %s", err) | ||
1145 | } | ||
1146 | if startIndex < 0 { | ||
1147 | return 0, 0, false, function.NewArgErrorf(1, "start index must not be less than zero") | ||
1148 | } | ||
1149 | if lengthKnown && startIndex > length { | ||
1150 | return 0, 0, false, function.NewArgErrorf(1, "start index must not be greater than the length of the list") | ||
1151 | } | ||
1152 | startKnown = true | ||
1153 | } | ||
1154 | if args[2].IsKnown() { | ||
1155 | if err := gocty.FromCtyValue(args[2], &endIndex); err != nil { | ||
1156 | return 0, 0, false, function.NewArgErrorf(2, "invalid end index: %s", err) | ||
1157 | } | ||
1158 | if endIndex < 0 { | ||
1159 | return 0, 0, false, function.NewArgErrorf(2, "end index must not be less than zero") | ||
1160 | } | ||
1161 | if lengthKnown && endIndex > length { | ||
1162 | return 0, 0, false, function.NewArgErrorf(2, "end index must not be greater than the length of the list") | ||
1163 | } | ||
1164 | endKnown = true | ||
1165 | } | ||
1166 | if startKnown && endKnown { | ||
1167 | if startIndex > endIndex { | ||
1168 | return 0, 0, false, function.NewArgErrorf(1, "start index must not be greater than end index") | ||
1169 | } | ||
1170 | } | ||
1171 | return startIndex, endIndex, startKnown && endKnown, nil | ||
1172 | } | ||
1173 | |||
1174 | // TransposeFunc contructs a function that takes a map of lists of strings and | ||
1175 | // TransposeFunc constructs a function that takes a map of lists of strings and | ||
1176 | // swaps the keys and values to produce a new map of lists of strings. | ||
1177 | var TransposeFunc = function.New(&function.Spec{ | ||
1178 | Params: []function.Parameter{ | ||
1179 | { | ||
1180 | Name: "values", | ||
1181 | Type: cty.Map(cty.List(cty.String)), | ||
1182 | }, | ||
1183 | }, | ||
1184 | Type: function.StaticReturnType(cty.Map(cty.List(cty.String))), | ||
1185 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
1186 | inputMap := args[0] | ||
1187 | if !inputMap.IsWhollyKnown() { | ||
1188 | return cty.UnknownVal(retType), nil | ||
1189 | } | ||
1190 | |||
1191 | outputMap := make(map[string]cty.Value) | ||
1192 | tmpMap := make(map[string][]string) | ||
1193 | |||
1194 | for it := inputMap.ElementIterator(); it.Next(); { | ||
1195 | inKey, inVal := it.Element() | ||
1196 | for iter := inVal.ElementIterator(); iter.Next(); { | ||
1197 | _, val := iter.Element() | ||
1198 | if !val.Type().Equals(cty.String) { | ||
1199 | return cty.MapValEmpty(cty.List(cty.String)), errors.New("input must be a map of lists of strings") | ||
1200 | } | ||
1201 | |||
1202 | outKey := val.AsString() | ||
1203 | if _, ok := tmpMap[outKey]; !ok { | ||
1204 | tmpMap[outKey] = make([]string, 0) | ||
1205 | } | ||
1206 | outVal := tmpMap[outKey] | ||
1207 | outVal = append(outVal, inKey.AsString()) | ||
1208 | sort.Strings(outVal) | ||
1209 | tmpMap[outKey] = outVal | ||
1210 | } | ||
1211 | } | ||
1212 | |||
1213 | for outKey, outVal := range tmpMap { | ||
1214 | values := make([]cty.Value, 0) | ||
1215 | for _, v := range outVal { | ||
1216 | values = append(values, cty.StringVal(v)) | ||
1217 | } | ||
1218 | outputMap[outKey] = cty.ListVal(values) | ||
1219 | } | ||
1220 | |||
1221 | return cty.MapVal(outputMap), nil | ||
1222 | }, | ||
1223 | }) | ||
1224 | |||
1225 | // ValuesFunc constructs a function that returns a list of the map values, | ||
1226 | // in the order of the sorted keys. | ||
1227 | var ValuesFunc = function.New(&function.Spec{ | ||
1228 | Params: []function.Parameter{ | ||
1229 | { | ||
1230 | Name: "values", | ||
1231 | Type: cty.DynamicPseudoType, | ||
1232 | }, | ||
1233 | }, | ||
1234 | Type: func(args []cty.Value) (ret cty.Type, err error) { | ||
1235 | ty := args[0].Type() | ||
1236 | if ty.IsMapType() { | ||
1237 | return cty.List(ty.ElementType()), nil | ||
1238 | } else if ty.IsObjectType() { | ||
1239 | // The result is a tuple type with all of the same types as our | ||
1240 | // object type's attributes, sorted in lexicographical order by the | ||
1241 | // keys. (This matches the sort order guaranteed by ElementIterator | ||
1242 | // on a cty object value.) | ||
1243 | atys := ty.AttributeTypes() | ||
1244 | if len(atys) == 0 { | ||
1245 | return cty.EmptyTuple, nil | ||
1246 | } | ||
1247 | attrNames := make([]string, 0, len(atys)) | ||
1248 | for name := range atys { | ||
1249 | attrNames = append(attrNames, name) | ||
1250 | } | ||
1251 | sort.Strings(attrNames) | ||
1252 | |||
1253 | tys := make([]cty.Type, len(attrNames)) | ||
1254 | for i, name := range attrNames { | ||
1255 | tys[i] = atys[name] | ||
1256 | } | ||
1257 | return cty.Tuple(tys), nil | ||
1258 | } | ||
1259 | return cty.NilType, errors.New("values() requires a map as the first argument") | ||
1260 | }, | ||
1261 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
1262 | mapVar := args[0] | ||
1263 | |||
1264 | // We can just iterate the map/object value here because cty guarantees | ||
1265 | // that these types always iterate in key lexicographical order. | ||
1266 | var values []cty.Value | ||
1267 | for it := mapVar.ElementIterator(); it.Next(); { | ||
1268 | _, val := it.Element() | ||
1269 | values = append(values, val) | ||
1270 | } | ||
1271 | |||
1272 | if retType.IsTupleType() { | ||
1273 | return cty.TupleVal(values), nil | ||
1274 | } | ||
1275 | if len(values) == 0 { | ||
1276 | return cty.ListValEmpty(retType.ElementType()), nil | ||
1277 | } | ||
1278 | return cty.ListVal(values), nil | ||
1279 | }, | ||
1280 | }) | ||
1281 | |||
1282 | // ZipmapFunc constructs a function that constructs a map from a list of keys | ||
1283 | // and a corresponding list of values. | ||
1284 | var ZipmapFunc = function.New(&function.Spec{ | ||
1285 | Params: []function.Parameter{ | ||
1286 | { | ||
1287 | Name: "keys", | ||
1288 | Type: cty.List(cty.String), | ||
1289 | }, | ||
1290 | { | ||
1291 | Name: "values", | ||
1292 | Type: cty.DynamicPseudoType, | ||
1293 | }, | ||
1294 | }, | ||
1295 | Type: func(args []cty.Value) (ret cty.Type, err error) { | ||
1296 | keys := args[0] | ||
1297 | values := args[1] | ||
1298 | valuesTy := values.Type() | ||
1299 | |||
1300 | switch { | ||
1301 | case valuesTy.IsListType(): | ||
1302 | return cty.Map(values.Type().ElementType()), nil | ||
1303 | case valuesTy.IsTupleType(): | ||
1304 | if !keys.IsWhollyKnown() { | ||
1305 | // Since zipmap with a tuple produces an object, we need to know | ||
1306 | // all of the key names before we can predict our result type. | ||
1307 | return cty.DynamicPseudoType, nil | ||
1308 | } | ||
1309 | |||
1310 | keysRaw := keys.AsValueSlice() | ||
1311 | valueTypesRaw := valuesTy.TupleElementTypes() | ||
1312 | if len(keysRaw) != len(valueTypesRaw) { | ||
1313 | return cty.NilType, fmt.Errorf("number of keys (%d) does not match number of values (%d)", len(keysRaw), len(valueTypesRaw)) | ||
1314 | } | ||
1315 | atys := make(map[string]cty.Type, len(valueTypesRaw)) | ||
1316 | for i, keyVal := range keysRaw { | ||
1317 | if keyVal.IsNull() { | ||
1318 | return cty.NilType, fmt.Errorf("keys list has null value at index %d", i) | ||
1319 | } | ||
1320 | key := keyVal.AsString() | ||
1321 | atys[key] = valueTypesRaw[i] | ||
1322 | } | ||
1323 | return cty.Object(atys), nil | ||
1324 | |||
1325 | default: | ||
1326 | return cty.NilType, errors.New("values argument must be a list or tuple value") | ||
1327 | } | ||
1328 | }, | ||
1329 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
1330 | keys := args[0] | ||
1331 | values := args[1] | ||
1332 | |||
1333 | if !keys.IsWhollyKnown() { | ||
1334 | // Unknown map keys and object attributes are not supported, so | ||
1335 | // our entire result must be unknown in this case. | ||
1336 | return cty.UnknownVal(retType), nil | ||
1337 | } | ||
1338 | |||
1339 | // both keys and values are guaranteed to be shallowly-known here, | ||
1340 | // because our declared params above don't allow unknown or null values. | ||
1341 | if keys.LengthInt() != values.LengthInt() { | ||
1342 | return cty.NilVal, fmt.Errorf("number of keys (%d) does not match number of values (%d)", keys.LengthInt(), values.LengthInt()) | ||
1343 | } | ||
1344 | |||
1345 | output := make(map[string]cty.Value) | ||
1346 | |||
1347 | i := 0 | ||
1348 | for it := keys.ElementIterator(); it.Next(); { | ||
1349 | _, v := it.Element() | ||
1350 | val := values.Index(cty.NumberIntVal(int64(i))) | ||
1351 | output[v.AsString()] = val | ||
1352 | i++ | ||
1353 | } | ||
1354 | |||
1355 | switch { | ||
1356 | case retType.IsMapType(): | ||
1357 | if len(output) == 0 { | ||
1358 | return cty.MapValEmpty(retType.ElementType()), nil | ||
1359 | } | ||
1360 | return cty.MapVal(output), nil | ||
1361 | case retType.IsObjectType(): | ||
1362 | return cty.ObjectVal(output), nil | ||
1363 | default: | ||
1364 | // Should never happen because the type-check function should've | ||
1365 | // caught any other case. | ||
1366 | return cty.NilVal, fmt.Errorf("internally selected incorrect result type %s (this is a bug)", retType.FriendlyName()) | ||
1367 | } | ||
1368 | }, | ||
1369 | }) | ||
1370 | |||
1371 | // helper function to add an element to a list, if it does not already exist | ||
1372 | func appendIfMissing(slice []cty.Value, element cty.Value) ([]cty.Value, error) { | ||
1373 | for _, ele := range slice { | ||
1374 | eq, err := stdlib.Equal(ele, element) | ||
1375 | if err != nil { | ||
1376 | return slice, err | ||
1377 | } | ||
1378 | if eq.True() { | ||
1379 | return slice, nil | ||
1380 | } | ||
1381 | } | ||
1382 | return append(slice, element), nil | ||
1383 | } | ||
1384 | |||
1385 | // Element returns a single element from a given list at the given index. If | ||
1386 | // index is greater than the length of the list then it is wrapped modulo | ||
1387 | // the list length. | ||
1388 | func Element(list, index cty.Value) (cty.Value, error) { | ||
1389 | return ElementFunc.Call([]cty.Value{list, index}) | ||
1390 | } | ||
1391 | |||
1392 | // Length returns the number of elements in the given collection or number of | ||
1393 | // Unicode characters in the given string. | ||
1394 | func Length(collection cty.Value) (cty.Value, error) { | ||
1395 | return LengthFunc.Call([]cty.Value{collection}) | ||
1396 | } | ||
1397 | |||
1398 | // Coalesce takes any number of arguments and returns the first one that isn't empty. | ||
1399 | func Coalesce(args ...cty.Value) (cty.Value, error) { | ||
1400 | return CoalesceFunc.Call(args) | ||
1401 | } | ||
1402 | |||
1403 | // CoalesceList takes any number of list arguments and returns the first one that isn't empty. | ||
1404 | func CoalesceList(args ...cty.Value) (cty.Value, error) { | ||
1405 | return CoalesceListFunc.Call(args) | ||
1406 | } | ||
1407 | |||
1408 | // Compact takes a list of strings and returns a new list | ||
1409 | // with any empty string elements removed. | ||
1410 | func Compact(list cty.Value) (cty.Value, error) { | ||
1411 | return CompactFunc.Call([]cty.Value{list}) | ||
1412 | } | ||
1413 | |||
1414 | // Contains determines whether a given list contains a given single value | ||
1415 | // as one of its elements. | ||
1416 | func Contains(list, value cty.Value) (cty.Value, error) { | ||
1417 | return ContainsFunc.Call([]cty.Value{list, value}) | ||
1418 | } | ||
1419 | |||
1420 | // Index finds the element index for a given value in a list. | ||
1421 | func Index(list, value cty.Value) (cty.Value, error) { | ||
1422 | return IndexFunc.Call([]cty.Value{list, value}) | ||
1423 | } | ||
1424 | |||
1425 | // Distinct takes a list and returns a new list with any duplicate elements removed. | ||
1426 | func Distinct(list cty.Value) (cty.Value, error) { | ||
1427 | return DistinctFunc.Call([]cty.Value{list}) | ||
1428 | } | ||
1429 | |||
1430 | // Chunklist splits a single list into fixed-size chunks, returning a list of lists. | ||
1431 | func Chunklist(list, size cty.Value) (cty.Value, error) { | ||
1432 | return ChunklistFunc.Call([]cty.Value{list, size}) | ||
1433 | } | ||
1434 | |||
1435 | // Flatten takes a list and replaces any elements that are lists with a flattened | ||
1436 | // sequence of the list contents. | ||
1437 | func Flatten(list cty.Value) (cty.Value, error) { | ||
1438 | return FlattenFunc.Call([]cty.Value{list}) | ||
1439 | } | ||
1440 | |||
1441 | // Keys takes a map and returns a sorted list of the map keys. | ||
1442 | func Keys(inputMap cty.Value) (cty.Value, error) { | ||
1443 | return KeysFunc.Call([]cty.Value{inputMap}) | ||
1444 | } | ||
1445 | |||
1446 | // List takes any number of list arguments and returns a list containing those | ||
1447 | // values in the same order. | ||
1448 | func List(args ...cty.Value) (cty.Value, error) { | ||
1449 | return ListFunc.Call(args) | ||
1450 | } | ||
1451 | |||
1452 | // Lookup performs a dynamic lookup into a map. | ||
1453 | // There are two required arguments, map and key, plus an optional default, | ||
1454 | // which is a value to return if no key is found in map. | ||
1455 | func Lookup(args ...cty.Value) (cty.Value, error) { | ||
1456 | return LookupFunc.Call(args) | ||
1457 | } | ||
1458 | |||
1459 | // Map takes an even number of arguments and returns a map whose elements are constructed | ||
1460 | // from consecutive pairs of arguments. | ||
1461 | func Map(args ...cty.Value) (cty.Value, error) { | ||
1462 | return MapFunc.Call(args) | ||
1463 | } | ||
1464 | |||
1465 | // Matchkeys constructs a new list by taking a subset of elements from one list | ||
1466 | // whose indexes match the corresponding indexes of values in another list. | ||
1467 | func Matchkeys(values, keys, searchset cty.Value) (cty.Value, error) { | ||
1468 | return MatchkeysFunc.Call([]cty.Value{values, keys, searchset}) | ||
1469 | } | ||
1470 | |||
1471 | // Merge takes an arbitrary number of maps and returns a single map that contains | ||
1472 | // a merged set of elements from all of the maps. | ||
1473 | // | ||
1474 | // If more than one given map defines the same key then the one that is later in | ||
1475 | // the argument sequence takes precedence. | ||
1476 | func Merge(maps ...cty.Value) (cty.Value, error) { | ||
1477 | return MergeFunc.Call(maps) | ||
1478 | } | ||
1479 | |||
1480 | // Reverse takes a sequence and produces a new sequence of the same length | ||
1481 | // with all of the same elements as the given sequence but in reverse order. | ||
1482 | func Reverse(list cty.Value) (cty.Value, error) { | ||
1483 | return ReverseFunc.Call([]cty.Value{list}) | ||
1484 | } | ||
1485 | |||
1486 | // SetProduct computes the cartesian product of sets or sequences. | ||
1487 | func SetProduct(sets ...cty.Value) (cty.Value, error) { | ||
1488 | return SetProductFunc.Call(sets) | ||
1489 | } | ||
1490 | |||
1491 | // Slice extracts some consecutive elements from within a list. | ||
1492 | func Slice(list, start, end cty.Value) (cty.Value, error) { | ||
1493 | return SliceFunc.Call([]cty.Value{list, start, end}) | ||
1494 | } | ||
1495 | |||
1496 | // Transpose takes a map of lists of strings and swaps the keys and values to | ||
1497 | // produce a new map of lists of strings. | ||
1498 | func Transpose(values cty.Value) (cty.Value, error) { | ||
1499 | return TransposeFunc.Call([]cty.Value{values}) | ||
1500 | } | ||
1501 | |||
1502 | // Values returns a list of the map values, in the order of the sorted keys. | ||
1503 | // This function only works on flat maps. | ||
1504 | func Values(values cty.Value) (cty.Value, error) { | ||
1505 | return ValuesFunc.Call([]cty.Value{values}) | ||
1506 | } | ||
1507 | |||
1508 | // Zipmap constructs a map from a list of keys and a corresponding list of values. | ||
1509 | func Zipmap(keys, values cty.Value) (cty.Value, error) { | ||
1510 | return ZipmapFunc.Call([]cty.Value{keys, values}) | ||
1511 | } | ||