]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / lang / funcs / conversion.go
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 }