]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/zclconf/go-cty/cty/function/function.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / zclconf / go-cty / cty / function / function.go
1 package function
2
3 import (
4 "fmt"
5
6 "github.com/zclconf/go-cty/cty"
7 )
8
9 // Function represents a function. This is the main type in this package.
10 type Function struct {
11 spec *Spec
12 }
13
14 // Spec is the specification of a function, used to instantiate
15 // a new Function.
16 type Spec struct {
17 // Params is a description of the positional parameters for the function.
18 // The standard checking logic rejects any calls that do not provide
19 // arguments conforming to this definition, freeing the function
20 // implementer from dealing with such inconsistencies.
21 Params []Parameter
22
23 // VarParam is an optional specification of additional "varargs" the
24 // function accepts. If this is non-nil then callers may provide an
25 // arbitrary number of additional arguments (after those matching with
26 // the fixed parameters in Params) that conform to the given specification,
27 // which will appear as additional values in the slices of values
28 // provided to the type and implementation functions.
29 VarParam *Parameter
30
31 // Type is the TypeFunc that decides the return type of the function
32 // given its arguments, which may be Unknown. See the documentation
33 // of TypeFunc for more information.
34 //
35 // Use StaticReturnType if the function's return type does not vary
36 // depending on its arguments.
37 Type TypeFunc
38
39 // Impl is the ImplFunc that implements the function's behavior.
40 //
41 // Functions are expected to behave as pure functions, and not create
42 // any visible side-effects.
43 //
44 // If a TypeFunc is also provided, the value returned from Impl *must*
45 // conform to the type it returns, or a call to the function will panic.
46 Impl ImplFunc
47 }
48
49 // New creates a new function with the given specification.
50 //
51 // After passing a Spec to this function, the caller must no longer read from
52 // or mutate it.
53 func New(spec *Spec) Function {
54 f := Function{
55 spec: spec,
56 }
57 return f
58 }
59
60 // TypeFunc is a callback type for determining the return type of a function
61 // given its arguments.
62 //
63 // Any of the values passed to this function may be unknown, even if the
64 // parameters are not configured to accept unknowns.
65 //
66 // If any of the given values are *not* unknown, the TypeFunc may use the
67 // values for pre-validation and for choosing the return type. For example,
68 // a hypothetical JSON-unmarshalling function could return
69 // cty.DynamicPseudoType if the given JSON string is unknown, but return
70 // a concrete type based on the JSON structure if the JSON string is already
71 // known.
72 type TypeFunc func(args []cty.Value) (cty.Type, error)
73
74 // ImplFunc is a callback type for the main implementation of a function.
75 //
76 // "args" are the values for the arguments, and this slice will always be at
77 // least as long as the argument definition slice for the function.
78 //
79 // "retType" is the type returned from the Type callback, included as a
80 // convenience to avoid the need to re-compute the return type for generic
81 // functions whose return type is a function of the arguments.
82 type ImplFunc func(args []cty.Value, retType cty.Type) (cty.Value, error)
83
84 // StaticReturnType returns a TypeFunc that always returns the given type.
85 //
86 // This is provided as a convenience for defining a function whose return
87 // type does not depend on the argument types.
88 func StaticReturnType(ty cty.Type) TypeFunc {
89 return func([]cty.Value) (cty.Type, error) {
90 return ty, nil
91 }
92 }
93
94 // ReturnType returns the return type of a function given a set of candidate
95 // argument types, or returns an error if the given types are unacceptable.
96 //
97 // If the caller already knows values for at least some of the arguments
98 // it can be better to call ReturnTypeForValues, since certain functions may
99 // determine their return types from their values and return DynamicVal if
100 // the values are unknown.
101 func (f Function) ReturnType(argTypes []cty.Type) (cty.Type, error) {
102 vals := make([]cty.Value, len(argTypes))
103 for i, ty := range argTypes {
104 vals[i] = cty.UnknownVal(ty)
105 }
106 return f.ReturnTypeForValues(vals)
107 }
108
109 // ReturnTypeForValues is similar to ReturnType but can be used if the caller
110 // already knows the values of some or all of the arguments, in which case
111 // the function may be able to determine a more definite result if its
112 // return type depends on the argument *values*.
113 //
114 // For any arguments whose values are not known, pass an Unknown value of
115 // the appropriate type.
116 func (f Function) ReturnTypeForValues(args []cty.Value) (ty cty.Type, err error) {
117 var posArgs []cty.Value
118 var varArgs []cty.Value
119
120 if f.spec.VarParam == nil {
121 if len(args) != len(f.spec.Params) {
122 return cty.Type{}, fmt.Errorf(
123 "wrong number of arguments (%d required; %d given)",
124 len(f.spec.Params), len(args),
125 )
126 }
127
128 posArgs = args
129 varArgs = nil
130 } else {
131 if len(args) < len(f.spec.Params) {
132 return cty.Type{}, fmt.Errorf(
133 "wrong number of arguments (at least %d required; %d given)",
134 len(f.spec.Params), len(args),
135 )
136 }
137
138 posArgs = args[0:len(f.spec.Params)]
139 varArgs = args[len(f.spec.Params):]
140 }
141
142 for i, spec := range f.spec.Params {
143 val := posArgs[i]
144
145 if val.IsNull() && !spec.AllowNull {
146 return cty.Type{}, NewArgErrorf(i, "argument must not be null")
147 }
148
149 // AllowUnknown is ignored for type-checking, since we expect to be
150 // able to type check with unknown values. We *do* still need to deal
151 // with DynamicPseudoType here though, since the Type function might
152 // not be ready to deal with that.
153
154 if val.Type() == cty.DynamicPseudoType {
155 if !spec.AllowDynamicType {
156 return cty.DynamicPseudoType, nil
157 }
158 } else if errs := val.Type().TestConformance(spec.Type); errs != nil {
159 // For now we'll just return the first error in the set, since
160 // we don't have a good way to return the whole list here.
161 // Would be good to do something better at some point...
162 return cty.Type{}, NewArgError(i, errs[0])
163 }
164 }
165
166 if varArgs != nil {
167 spec := f.spec.VarParam
168 for i, val := range varArgs {
169 realI := i + len(posArgs)
170
171 if val.IsNull() && !spec.AllowNull {
172 return cty.Type{}, NewArgErrorf(realI, "argument must not be null")
173 }
174
175 if val.Type() == cty.DynamicPseudoType {
176 if !spec.AllowDynamicType {
177 return cty.DynamicPseudoType, nil
178 }
179 } else if errs := val.Type().TestConformance(spec.Type); errs != nil {
180 // For now we'll just return the first error in the set, since
181 // we don't have a good way to return the whole list here.
182 // Would be good to do something better at some point...
183 return cty.Type{}, NewArgError(i, errs[0])
184 }
185 }
186 }
187
188 // Intercept any panics from the function and return them as normal errors,
189 // so a calling language runtime doesn't need to deal with panics.
190 defer func() {
191 if r := recover(); r != nil {
192 ty = cty.NilType
193 err = errorForPanic(r)
194 }
195 }()
196
197 return f.spec.Type(args)
198 }
199
200 // Call actually calls the function with the given arguments, which must
201 // conform to the function's parameter specification or an error will be
202 // returned.
203 func (f Function) Call(args []cty.Value) (val cty.Value, err error) {
204 expectedType, err := f.ReturnTypeForValues(args)
205 if err != nil {
206 return cty.NilVal, err
207 }
208
209 // Type checking already dealt with most situations relating to our
210 // parameter specification, but we still need to deal with unknown
211 // values.
212 posArgs := args[:len(f.spec.Params)]
213 varArgs := args[len(f.spec.Params):]
214
215 for i, spec := range f.spec.Params {
216 val := posArgs[i]
217
218 if !val.IsKnown() && !spec.AllowUnknown {
219 return cty.UnknownVal(expectedType), nil
220 }
221 }
222
223 if f.spec.VarParam != nil {
224 spec := f.spec.VarParam
225 for _, val := range varArgs {
226 if !val.IsKnown() && !spec.AllowUnknown {
227 return cty.UnknownVal(expectedType), nil
228 }
229 }
230 }
231
232 var retVal cty.Value
233 {
234 // Intercept any panics from the function and return them as normal errors,
235 // so a calling language runtime doesn't need to deal with panics.
236 defer func() {
237 if r := recover(); r != nil {
238 val = cty.NilVal
239 err = errorForPanic(r)
240 }
241 }()
242
243 retVal, err = f.spec.Impl(args, expectedType)
244 if err != nil {
245 return cty.NilVal, err
246 }
247 }
248
249 // Returned value must conform to what the Type function expected, to
250 // protect callers from having to deal with inconsistencies.
251 if errs := retVal.Type().TestConformance(expectedType); errs != nil {
252 panic(fmt.Errorf(
253 "returned value %#v does not conform to expected return type %#v: %s",
254 retVal, expectedType, errs[0],
255 ))
256 }
257
258 return retVal, nil
259 }
260
261 // ProxyFunc the type returned by the method Function.Proxy.
262 type ProxyFunc func(args ...cty.Value) (cty.Value, error)
263
264 // Proxy returns a function that can be called with cty.Value arguments
265 // to run the function. This is provided as a convenience for when using
266 // a function directly within Go code.
267 func (f Function) Proxy() ProxyFunc {
268 return func(args ...cty.Value) (cty.Value, error) {
269 return f.Call(args)
270 }
271 }
272
273 // Params returns information about the function's fixed positional parameters.
274 // This does not include information about any variadic arguments accepted;
275 // for that, call VarParam.
276 func (f Function) Params() []Parameter {
277 new := make([]Parameter, len(f.spec.Params))
278 copy(new, f.spec.Params)
279 return new
280 }
281
282 // VarParam returns information about the variadic arguments the function
283 // expects, or nil if the function is not variadic.
284 func (f Function) VarParam() *Parameter {
285 if f.spec.VarParam == nil {
286 return nil
287 }
288
289 ret := *f.spec.VarParam
290 return &ret
291 }