diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go b/vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go new file mode 100644 index 0000000..83f8597 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go | |||
@@ -0,0 +1,87 @@ | |||
1 | package funcs | ||
2 | |||
3 | import ( | ||
4 | "strconv" | ||
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 | // MakeToFunc constructs a "to..." function, like "tostring", which converts | ||
12 | // its argument to a specific type or type kind. | ||
13 | // | ||
14 | // The given type wantTy can be any type constraint that cty's "convert" package | ||
15 | // would accept. In particular, this means that you can pass | ||
16 | // cty.List(cty.DynamicPseudoType) to mean "list of any single type", which | ||
17 | // will then cause cty to attempt to unify all of the element types when given | ||
18 | // a tuple. | ||
19 | func MakeToFunc(wantTy cty.Type) function.Function { | ||
20 | return function.New(&function.Spec{ | ||
21 | Params: []function.Parameter{ | ||
22 | { | ||
23 | Name: "v", | ||
24 | // We use DynamicPseudoType rather than wantTy here so that | ||
25 | // all values will pass through the function API verbatim and | ||
26 | // we can handle the conversion logic within the Type and | ||
27 | // Impl functions. This allows us to customize the error | ||
28 | // messages to be more appropriate for an explicit type | ||
29 | // conversion, whereas the cty function system produces | ||
30 | // messages aimed at _implicit_ type conversions. | ||
31 | Type: cty.DynamicPseudoType, | ||
32 | AllowNull: true, | ||
33 | }, | ||
34 | }, | ||
35 | Type: func(args []cty.Value) (cty.Type, error) { | ||
36 | gotTy := args[0].Type() | ||
37 | if gotTy.Equals(wantTy) { | ||
38 | return wantTy, nil | ||
39 | } | ||
40 | conv := convert.GetConversionUnsafe(args[0].Type(), wantTy) | ||
41 | if conv == nil { | ||
42 | // We'll use some specialized errors for some trickier cases, | ||
43 | // but most we can handle in a simple way. | ||
44 | switch { | ||
45 | case gotTy.IsTupleType() && wantTy.IsTupleType(): | ||
46 | return cty.NilType, function.NewArgErrorf(0, "incompatible tuple type for conversion: %s", convert.MismatchMessage(gotTy, wantTy)) | ||
47 | case gotTy.IsObjectType() && wantTy.IsObjectType(): | ||
48 | return cty.NilType, function.NewArgErrorf(0, "incompatible object type for conversion: %s", convert.MismatchMessage(gotTy, wantTy)) | ||
49 | default: | ||
50 | return cty.NilType, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) | ||
51 | } | ||
52 | } | ||
53 | // If a conversion is available then everything is fine. | ||
54 | return wantTy, nil | ||
55 | }, | ||
56 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | ||
57 | // We didn't set "AllowUnknown" on our argument, so it is guaranteed | ||
58 | // to be known here but may still be null. | ||
59 | ret, err := convert.Convert(args[0], retType) | ||
60 | if err != nil { | ||
61 | // Because we used GetConversionUnsafe above, conversion can | ||
62 | // still potentially fail in here. For example, if the user | ||
63 | // asks to convert the string "a" to bool then we'll | ||
64 | // optimistically permit it during type checking but fail here | ||
65 | // once we note that the value isn't either "true" or "false". | ||
66 | gotTy := args[0].Type() | ||
67 | switch { | ||
68 | case gotTy == cty.String && wantTy == cty.Bool: | ||
69 | what := "string" | ||
70 | if !args[0].IsNull() { | ||
71 | what = strconv.Quote(args[0].AsString()) | ||
72 | } | ||
73 | return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to bool; only the strings "true" or "false" are allowed`, what) | ||
74 | case gotTy == cty.String && wantTy == cty.Number: | ||
75 | what := "string" | ||
76 | if !args[0].IsNull() { | ||
77 | what = strconv.Quote(args[0].AsString()) | ||
78 | } | ||
79 | return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to number; given string must be a decimal representation of a number`, what) | ||
80 | default: | ||
81 | return cty.NilVal, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) | ||
82 | } | ||
83 | } | ||
84 | return ret, nil | ||
85 | }, | ||
86 | }) | ||
87 | } | ||