]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - 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
CommitLineData
15c0b25d
AP
1package function
2
3import (
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.
10type Function struct {
11 spec *Spec
12}
13
14// Spec is the specification of a function, used to instantiate
15// a new Function.
16type 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.
53func 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.
72type 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.
82type 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.
88func 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.
101func (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.
116func (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 {
107c1cdb 146 return cty.Type{}, NewArgErrorf(i, "argument must not be null")
15c0b25d
AP
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 {
107c1cdb 172 return cty.Type{}, NewArgErrorf(realI, "argument must not be null")
15c0b25d
AP
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.
203func (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.
262type 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.
267func (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.
276func (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.
284func (f Function) VarParam() *Parameter {
285 if f.spec.VarParam == nil {
286 return nil
287 }
288
289 ret := *f.spec.VarParam
290 return &ret
291}