github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/lang/funcs/conversion.go (about) 1 package funcs 2 3 import ( 4 "strconv" 5 6 "github.com/hashicorp/terraform/internal/lang/marks" 7 "github.com/hashicorp/terraform/internal/lang/types" 8 "github.com/zclconf/go-cty/cty" 9 "github.com/zclconf/go-cty/cty/convert" 10 "github.com/zclconf/go-cty/cty/function" 11 ) 12 13 // MakeToFunc constructs a "to..." function, like "tostring", which converts 14 // its argument to a specific type or type kind. 15 // 16 // The given type wantTy can be any type constraint that cty's "convert" package 17 // would accept. In particular, this means that you can pass 18 // cty.List(cty.DynamicPseudoType) to mean "list of any single type", which 19 // will then cause cty to attempt to unify all of the element types when given 20 // a tuple. 21 func MakeToFunc(wantTy cty.Type) function.Function { 22 return function.New(&function.Spec{ 23 Params: []function.Parameter{ 24 { 25 Name: "v", 26 // We use DynamicPseudoType rather than wantTy here so that 27 // all values will pass through the function API verbatim and 28 // we can handle the conversion logic within the Type and 29 // Impl functions. This allows us to customize the error 30 // messages to be more appropriate for an explicit type 31 // conversion, whereas the cty function system produces 32 // messages aimed at _implicit_ type conversions. 33 Type: cty.DynamicPseudoType, 34 AllowNull: true, 35 AllowMarked: true, 36 AllowDynamicType: true, 37 }, 38 }, 39 Type: func(args []cty.Value) (cty.Type, error) { 40 gotTy := args[0].Type() 41 if gotTy.Equals(wantTy) { 42 return wantTy, nil 43 } 44 conv := convert.GetConversionUnsafe(args[0].Type(), wantTy) 45 if conv == nil { 46 // We'll use some specialized errors for some trickier cases, 47 // but most we can handle in a simple way. 48 switch { 49 case gotTy.IsTupleType() && wantTy.IsTupleType(): 50 return cty.NilType, function.NewArgErrorf(0, "incompatible tuple type for conversion: %s", convert.MismatchMessage(gotTy, wantTy)) 51 case gotTy.IsObjectType() && wantTy.IsObjectType(): 52 return cty.NilType, function.NewArgErrorf(0, "incompatible object type for conversion: %s", convert.MismatchMessage(gotTy, wantTy)) 53 default: 54 return cty.NilType, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) 55 } 56 } 57 // If a conversion is available then everything is fine. 58 return wantTy, nil 59 }, 60 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 61 // We didn't set "AllowUnknown" on our argument, so it is guaranteed 62 // to be known here but may still be null. 63 ret, err := convert.Convert(args[0], retType) 64 if err != nil { 65 val, _ := args[0].UnmarkDeep() 66 // Because we used GetConversionUnsafe above, conversion can 67 // still potentially fail in here. For example, if the user 68 // asks to convert the string "a" to bool then we'll 69 // optimistically permit it during type checking but fail here 70 // once we note that the value isn't either "true" or "false". 71 gotTy := val.Type() 72 switch { 73 case marks.Contains(args[0], marks.Sensitive): 74 // Generic message so we won't inadvertently disclose 75 // information about sensitive values. 76 return cty.NilVal, function.NewArgErrorf(0, "cannot convert this sensitive %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) 77 78 case gotTy == cty.String && wantTy == cty.Bool: 79 what := "string" 80 if !val.IsNull() { 81 what = strconv.Quote(val.AsString()) 82 } 83 return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to bool; only the strings "true" or "false" are allowed`, what) 84 case gotTy == cty.String && wantTy == cty.Number: 85 what := "string" 86 if !val.IsNull() { 87 what = strconv.Quote(val.AsString()) 88 } 89 return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to number; given string must be a decimal representation of a number`, what) 90 default: 91 return cty.NilVal, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) 92 } 93 } 94 return ret, nil 95 }, 96 }) 97 } 98 99 // TypeFunc returns an encapsulated value containing its argument's type. This 100 // value is marked to allow us to limit the use of this function at the moment 101 // to only a few supported use cases. 102 var TypeFunc = function.New(&function.Spec{ 103 Params: []function.Parameter{ 104 { 105 Name: "value", 106 Type: cty.DynamicPseudoType, 107 AllowDynamicType: true, 108 AllowUnknown: true, 109 AllowNull: true, 110 }, 111 }, 112 Type: function.StaticReturnType(types.TypeType), 113 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 114 givenType := args[0].Type() 115 return cty.CapsuleVal(types.TypeType, &givenType).Mark(marks.TypeType), nil 116 }, 117 }) 118 119 func Type(input []cty.Value) (cty.Value, error) { 120 return TypeFunc.Call(input) 121 }