diff options
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/function/function.go')
-rw-r--r-- | vendor/github.com/zclconf/go-cty/cty/function/function.go | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/function/function.go b/vendor/github.com/zclconf/go-cty/cty/function/function.go new file mode 100644 index 0000000..162f7bf --- /dev/null +++ b/vendor/github.com/zclconf/go-cty/cty/function/function.go | |||
@@ -0,0 +1,291 @@ | |||
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, "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, "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 | } | ||