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