6 "github.com/zclconf/go-cty/cty"
9 // Function represents a function. This is the main type in this package.
10 type Function struct {
14 // Spec is the specification of a function, used to instantiate
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.
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.
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.
35 // Use StaticReturnType if the function's return type does not vary
36 // depending on its arguments.
39 // Impl is the ImplFunc that implements the function's behavior.
41 // Functions are expected to behave as pure functions, and not create
42 // any visible side-effects.
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.
49 // New creates a new function with the given specification.
51 // After passing a Spec to this function, the caller must no longer read from
53 func New(spec *Spec) Function {
60 // TypeFunc is a callback type for determining the return type of a function
61 // given its arguments.
63 // Any of the values passed to this function may be unknown, even if the
64 // parameters are not configured to accept unknowns.
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
72 type TypeFunc func(args []cty.Value) (cty.Type, error)
74 // ImplFunc is a callback type for the main implementation of a function.
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.
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)
84 // StaticReturnType returns a TypeFunc that always returns the given type.
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) {
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.
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)
106 return f.ReturnTypeForValues(vals)
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*.
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
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),
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),
138 posArgs = args[0:len(f.spec.Params)]
139 varArgs = args[len(f.spec.Params):]
142 for i, spec := range f.spec.Params {
145 if val.IsNull() && !spec.AllowNull {
146 return cty.Type{}, NewArgErrorf(i, "argument must not be null")
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.
154 if val.Type() == cty.DynamicPseudoType {
155 if !spec.AllowDynamicType {
156 return cty.DynamicPseudoType, nil
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])
167 spec := f.spec.VarParam
168 for i, val := range varArgs {
169 realI := i + len(posArgs)
171 if val.IsNull() && !spec.AllowNull {
172 return cty.Type{}, NewArgErrorf(realI, "argument must not be null")
175 if val.Type() == cty.DynamicPseudoType {
176 if !spec.AllowDynamicType {
177 return cty.DynamicPseudoType, nil
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])
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.
191 if r := recover(); r != nil {
193 err = errorForPanic(r)
197 return f.spec.Type(args)
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
203 func (f Function) Call(args []cty.Value) (val cty.Value, err error) {
204 expectedType, err := f.ReturnTypeForValues(args)
206 return cty.NilVal, err
209 // Type checking already dealt with most situations relating to our
210 // parameter specification, but we still need to deal with unknown
212 posArgs := args[:len(f.spec.Params)]
213 varArgs := args[len(f.spec.Params):]
215 for i, spec := range f.spec.Params {
218 if !val.IsKnown() && !spec.AllowUnknown {
219 return cty.UnknownVal(expectedType), nil
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
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.
237 if r := recover(); r != nil {
239 err = errorForPanic(r)
243 retVal, err = f.spec.Impl(args, expectedType)
245 return cty.NilVal, err
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 {
253 "returned value %#v does not conform to expected return type %#v: %s",
254 retVal, expectedType, errs[0],
261 // ProxyFunc the type returned by the method Function.Proxy.
262 type ProxyFunc func(args ...cty.Value) (cty.Value, error)
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) {
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)
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 {
289 ret := *f.spec.VarParam