6 "github.com/zclconf/go-cty/cty"
7 "github.com/zclconf/go-cty/cty/convert"
8 "github.com/zclconf/go-cty/cty/function"
11 var ConcatFunc = function.New(&function.Spec{
12 Params: []function.Parameter{},
13 VarParam: &function.Parameter{
15 Type: cty.DynamicPseudoType,
17 Type: func(args []cty.Value) (ret cty.Type, err error) {
19 return cty.NilType, fmt.Errorf("at least one argument is required")
22 if args[0].Type().IsListType() {
23 // Possibly we're going to return a list, if all of our other
24 // args are also lists and we can find a common element type.
25 tys := make([]cty.Type, len(args))
26 for i, val := range args {
36 commonType, _ := convert.UnifyUnsafe(tys)
37 if commonType != cty.NilType {
38 return commonType, nil
43 etys := make([]cty.Type, 0, len(args))
44 for i, val := range args {
47 case ety.IsTupleType():
48 etys = append(etys, ety.TupleElementTypes()...)
49 case ety.IsListType():
51 // We need to know the list to count its elements to
52 // build our tuple type, so any concat of an unknown
53 // list can't be typed yet.
54 return cty.DynamicPseudoType, nil
58 subEty := ety.ElementType()
59 for j := 0; j < l; j++ {
60 etys = append(etys, subEty)
63 return cty.NilType, function.NewArgErrorf(
64 i, "all arguments must be lists or tuples; got %s",
69 return cty.Tuple(etys), nil
71 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
73 case retType.IsListType():
74 // If retType is a list type then we know that all of the
75 // given values will be lists and that they will either be of
76 // retType or of something we can convert to retType.
77 vals := make([]cty.Value, 0, len(args))
78 for i, list := range args {
79 list, err = convert.Convert(list, retType)
81 // Conversion might fail because we used UnifyUnsafe
82 // to choose our return type.
83 return cty.NilVal, function.NewArgError(i, err)
86 it := list.ElementIterator()
89 vals = append(vals, v)
93 return cty.ListValEmpty(retType.ElementType()), nil
96 return cty.ListVal(vals), nil
97 case retType.IsTupleType():
98 // If retType is a tuple type then we could have a mixture of
99 // lists and tuples but we know they all have known values
100 // (because our params don't AllowUnknown) and we know that
101 // concatenating them all together will produce a tuple of
102 // retType because of the work we did in the Type function above.
103 vals := make([]cty.Value, 0, len(args))
105 for _, seq := range args {
106 // Both lists and tuples support ElementIterator, so this is easy.
107 it := seq.ElementIterator()
110 vals = append(vals, v)
114 return cty.TupleVal(vals), nil
116 // should never happen if Type is working correctly above
117 panic("unsupported return type")
122 // Concat takes one or more sequences (lists or tuples) and returns the single
123 // sequence that results from concatenating them together in order.
125 // If all of the given sequences are lists of the same element type then the
126 // result is a list of that type. Otherwise, the result is a of a tuple type
127 // constructed from the given sequence types.
128 func Concat(seqs ...cty.Value) (cty.Value, error) {
129 return ConcatFunc.Call(seqs)