]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/zclconf/go-cty/cty/function/stdlib/sequence.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / zclconf / go-cty / cty / function / stdlib / sequence.go
1 package stdlib
2
3 import (
4 "fmt"
5
6 "github.com/zclconf/go-cty/cty"
7 "github.com/zclconf/go-cty/cty/convert"
8 "github.com/zclconf/go-cty/cty/function"
9 )
10
11 var ConcatFunc = function.New(&function.Spec{
12 Params: []function.Parameter{},
13 VarParam: &function.Parameter{
14 Name: "seqs",
15 Type: cty.DynamicPseudoType,
16 },
17 Type: func(args []cty.Value) (ret cty.Type, err error) {
18 if len(args) == 0 {
19 return cty.NilType, fmt.Errorf("at least one argument is required")
20 }
21
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 {
27 ty := val.Type()
28 if !ty.IsListType() {
29 tys = nil
30 break
31 }
32 tys[i] = ty
33 }
34
35 if tys != nil {
36 commonType, _ := convert.UnifyUnsafe(tys)
37 if commonType != cty.NilType {
38 return commonType, nil
39 }
40 }
41 }
42
43 etys := make([]cty.Type, 0, len(args))
44 for i, val := range args {
45 ety := val.Type()
46 switch {
47 case ety.IsTupleType():
48 etys = append(etys, ety.TupleElementTypes()...)
49 case ety.IsListType():
50 if !val.IsKnown() {
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
55 }
56
57 l := val.LengthInt()
58 subEty := ety.ElementType()
59 for j := 0; j < l; j++ {
60 etys = append(etys, subEty)
61 }
62 default:
63 return cty.NilType, function.NewArgErrorf(
64 i, "all arguments must be lists or tuples; got %s",
65 ety.FriendlyName(),
66 )
67 }
68 }
69 return cty.Tuple(etys), nil
70 },
71 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
72 switch {
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)
80 if err != nil {
81 // Conversion might fail because we used UnifyUnsafe
82 // to choose our return type.
83 return cty.NilVal, function.NewArgError(i, err)
84 }
85
86 it := list.ElementIterator()
87 for it.Next() {
88 _, v := it.Element()
89 vals = append(vals, v)
90 }
91 }
92 if len(vals) == 0 {
93 return cty.ListValEmpty(retType.ElementType()), nil
94 }
95
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))
104
105 for _, seq := range args {
106 // Both lists and tuples support ElementIterator, so this is easy.
107 it := seq.ElementIterator()
108 for it.Next() {
109 _, v := it.Element()
110 vals = append(vals, v)
111 }
112 }
113
114 return cty.TupleVal(vals), nil
115 default:
116 // should never happen if Type is working correctly above
117 panic("unsupported return type")
118 }
119 },
120 })
121
122 var RangeFunc = function.New(&function.Spec{
123 VarParam: &function.Parameter{
124 Name: "params",
125 Type: cty.Number,
126 },
127 Type: function.StaticReturnType(cty.List(cty.Number)),
128 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
129 var start, end, step cty.Value
130 switch len(args) {
131 case 1:
132 if args[0].LessThan(cty.Zero).True() {
133 start, end, step = cty.Zero, args[0], cty.NumberIntVal(-1)
134 } else {
135 start, end, step = cty.Zero, args[0], cty.NumberIntVal(1)
136 }
137 case 2:
138 if args[1].LessThan(args[0]).True() {
139 start, end, step = args[0], args[1], cty.NumberIntVal(-1)
140 } else {
141 start, end, step = args[0], args[1], cty.NumberIntVal(1)
142 }
143 case 3:
144 start, end, step = args[0], args[1], args[2]
145 default:
146 return cty.NilVal, fmt.Errorf("must have one, two, or three arguments")
147 }
148
149 var vals []cty.Value
150
151 if step == cty.Zero {
152 return cty.NilVal, function.NewArgErrorf(2, "step must not be zero")
153 }
154 down := step.LessThan(cty.Zero).True()
155
156 if down {
157 if end.GreaterThan(start).True() {
158 return cty.NilVal, function.NewArgErrorf(1, "end must be less than start when step is negative")
159 }
160 } else {
161 if end.LessThan(start).True() {
162 return cty.NilVal, function.NewArgErrorf(1, "end must be greater than start when step is positive")
163 }
164 }
165
166 num := start
167 for {
168 if down {
169 if num.LessThanOrEqualTo(end).True() {
170 break
171 }
172 } else {
173 if num.GreaterThanOrEqualTo(end).True() {
174 break
175 }
176 }
177 if len(vals) >= 1024 {
178 // Artificial limit to prevent bad arguments from consuming huge amounts of memory
179 return cty.NilVal, fmt.Errorf("more than 1024 values were generated; either decrease the difference between start and end or use a smaller step")
180 }
181 vals = append(vals, num)
182 num = num.Add(step)
183 }
184 if len(vals) == 0 {
185 return cty.ListValEmpty(cty.Number), nil
186 }
187 return cty.ListVal(vals), nil
188 },
189 })
190
191 // Concat takes one or more sequences (lists or tuples) and returns the single
192 // sequence that results from concatenating them together in order.
193 //
194 // If all of the given sequences are lists of the same element type then the
195 // result is a list of that type. Otherwise, the result is a of a tuple type
196 // constructed from the given sequence types.
197 func Concat(seqs ...cty.Value) (cty.Value, error) {
198 return ConcatFunc.Call(seqs)
199 }
200
201 // Range creates a list of numbers by starting from the given starting value,
202 // then adding the given step value until the result is greater than or
203 // equal to the given stopping value. Each intermediate result becomes an
204 // element in the resulting list.
205 //
206 // When all three parameters are set, the order is (start, end, step). If
207 // only two parameters are set, they are the start and end respectively and
208 // step defaults to 1. If only one argument is set, it gives the end value
209 // with start defaulting to 0 and step defaulting to 1.
210 //
211 // Because the resulting list must be fully buffered in memory, there is an
212 // artificial cap of 1024 elements, after which this function will return
213 // an error to avoid consuming unbounded amounts of memory. The Range function
214 // is primarily intended for creating small lists of indices to iterate over,
215 // so there should be no reason to generate huge lists with it.
216 func Range(params ...cty.Value) (cty.Value, error) {
217 return RangeFunc.Call(params)
218 }