github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/plans/dynamic_value.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package plans 5 6 import ( 7 "github.com/zclconf/go-cty/cty" 8 ctymsgpack "github.com/zclconf/go-cty/cty/msgpack" 9 ) 10 11 // DynamicValue is the representation in the plan of a value whose type cannot 12 // be determined at compile time, such as because it comes from a schema 13 // defined in a plugin. 14 // 15 // This type is used as an indirection so that the overall plan structure can 16 // be decoded without schema available, and then the dynamic values accessed 17 // at a later time once the appropriate schema has been determined. 18 // 19 // Internally, DynamicValue is a serialized version of a cty.Value created 20 // against a particular type constraint. Callers should not access directly 21 // the serialized form, whose format may change in future. Values of this 22 // type must always be created by calling NewDynamicValue. 23 // 24 // The zero value of DynamicValue is nil, and represents the absense of a 25 // value within the Go type system. This is distinct from a cty.NullVal 26 // result, which represents the absense of a value within the cty type system. 27 type DynamicValue []byte 28 29 // NewDynamicValue creates a DynamicValue by serializing the given value 30 // against the given type constraint. The value must conform to the type 31 // constraint, or the result is undefined. 32 // 33 // If the value to be encoded has no predefined schema (for example, for 34 // module output values and input variables), set the type constraint to 35 // cty.DynamicPseudoType in order to save type information as part of the 36 // value, and then also pass cty.DynamicPseudoType to method Decode to recover 37 // the original value. 38 // 39 // cty.NilVal can be used to represent the absense of a value, but callers 40 // must be careful to distinguish values that are absent at the Go layer 41 // (cty.NilVal) vs. values that are absent at the cty layer (cty.NullVal 42 // results). 43 func NewDynamicValue(val cty.Value, ty cty.Type) (DynamicValue, error) { 44 // If we're given cty.NilVal (the zero value of cty.Value, which is 45 // distinct from a typed null value created by cty.NullVal) then we'll 46 // assume the caller is trying to represent the _absense_ of a value, 47 // and so we'll return a nil DynamicValue. 48 if val == cty.NilVal { 49 return DynamicValue(nil), nil 50 } 51 52 // Currently our internal encoding is msgpack, via ctymsgpack. 53 buf, err := ctymsgpack.Marshal(val, ty) 54 if err != nil { 55 return nil, err 56 } 57 58 return DynamicValue(buf), nil 59 } 60 61 // Decode retrieves the effective value from the receiever by interpreting the 62 // serialized form against the given type constraint. For correct results, 63 // the type constraint must match (or be consistent with) the one that was 64 // used to create the receiver. 65 // 66 // A nil DynamicValue decodes to cty.NilVal, which is not a valid value and 67 // instead represents the absense of a value. 68 func (v DynamicValue) Decode(ty cty.Type) (cty.Value, error) { 69 if v == nil { 70 return cty.NilVal, nil 71 } 72 73 return ctymsgpack.Unmarshal([]byte(v), ty) 74 } 75 76 // ImpliedType returns the type implied by the serialized structure of the 77 // receiving value. 78 // 79 // This will not necessarily be exactly the type that was given when the 80 // value was encoded, and in particular must not be used for values that 81 // were encoded with their static type given as cty.DynamicPseudoType. 82 // It is however safe to use this method for values that were encoded using 83 // their runtime type as the conforming type, with the result being 84 // semantically equivalent but with all lists and sets represented as tuples, 85 // and maps as objects, due to ambiguities of the serialization. 86 func (v DynamicValue) ImpliedType() (cty.Type, error) { 87 return ctymsgpack.ImpliedType([]byte(v)) 88 } 89 90 // Copy produces a copy of the receiver with a distinct backing array. 91 func (v DynamicValue) Copy() DynamicValue { 92 if v == nil { 93 return nil 94 } 95 96 ret := make(DynamicValue, len(v)) 97 copy(ret, v) 98 return ret 99 }