8 "golang.org/x/text/unicode/norm"
10 "github.com/zclconf/go-cty/cty/set"
13 // BoolVal returns a Value of type Number whose internal value is the given
15 func BoolVal(v bool) Value {
22 // NumberVal returns a Value of type Number whose internal value is the given
23 // big.Float. The returned value becomes the owner of the big.Float object,
24 // and so it's forbidden for the caller to mutate the object after it's
25 // wrapped in this way.
26 func NumberVal(v *big.Float) Value {
33 // ParseNumberVal returns a Value of type number produced by parsing the given
34 // string as a decimal real number. To ensure that two identical strings will
35 // always produce an equal number, always use this function to derive a number
36 // from a string; it will ensure that the precision and rounding mode for the
37 // internal big decimal is configured in a consistent way.
39 // If the given string cannot be parsed as a number, the returned error has
40 // the message "a number is required", making it suitable to return to an
41 // end-user to signal a type conversion error.
43 // If the given string contains a number that becomes a recurring fraction
44 // when expressed in binary then it will be truncated to have a 512-bit
45 // mantissa. Note that this is a higher precision than that of a float64,
46 // so coverting the same decimal number first to float64 and then calling
47 // NumberFloatVal will not produce an equal result; the conversion first
48 // to float64 will round the mantissa to fewer than 512 bits.
49 func ParseNumberVal(s string) (Value, error) {
50 // Base 10, precision 512, and rounding to nearest even is the standard
51 // way to handle numbers arriving as strings.
52 f, _, err := big.ParseFloat(s, 10, 512, big.ToNearestEven)
54 return NilVal, fmt.Errorf("a number is required")
56 return NumberVal(f), nil
59 // MustParseNumberVal is like ParseNumberVal but it will panic in case of any
60 // error. It can be used during initialization or any other situation where
61 // the given string is a constant or otherwise known to be correct by the
63 func MustParseNumberVal(s string) Value {
64 ret, err := ParseNumberVal(s)
71 // NumberIntVal returns a Value of type Number whose internal value is equal
72 // to the given integer.
73 func NumberIntVal(v int64) Value {
74 return NumberVal(new(big.Float).SetInt64(v))
77 // NumberUIntVal returns a Value of type Number whose internal value is equal
78 // to the given unsigned integer.
79 func NumberUIntVal(v uint64) Value {
80 return NumberVal(new(big.Float).SetUint64(v))
83 // NumberFloatVal returns a Value of type Number whose internal value is
84 // equal to the given float.
85 func NumberFloatVal(v float64) Value {
86 return NumberVal(new(big.Float).SetFloat64(v))
89 // StringVal returns a Value of type String whose internal value is the
92 // Strings must be UTF-8 encoded sequences of valid unicode codepoints, and
93 // they are NFC-normalized on entry into the world of cty values.
95 // If the given string is not valid UTF-8 then behavior of string operations
97 func StringVal(v string) Value {
100 v: NormalizeString(v),
104 // NormalizeString applies the same normalization that cty applies when
105 // constructing string values.
107 // A return value from this function can be meaningfully compared byte-for-byte
108 // with a Value.AsString result.
109 func NormalizeString(s string) string {
110 return norm.NFC.String(s)
113 // ObjectVal returns a Value of an object type whose structure is defined
114 // by the key names and value types in the given map.
115 func ObjectVal(attrs map[string]Value) Value {
116 attrTypes := make(map[string]Type, len(attrs))
117 attrVals := make(map[string]interface{}, len(attrs))
119 for attr, val := range attrs {
120 attr = NormalizeString(attr)
121 attrTypes[attr] = val.ty
122 attrVals[attr] = val.v
126 ty: Object(attrTypes),
131 // TupleVal returns a Value of a tuple type whose element types are
132 // defined by the value types in the given slice.
133 func TupleVal(elems []Value) Value {
134 elemTypes := make([]Type, len(elems))
135 elemVals := make([]interface{}, len(elems))
137 for i, val := range elems {
138 elemTypes[i] = val.ty
143 ty: Tuple(elemTypes),
148 // ListVal returns a Value of list type whose element type is defined by
149 // the types of the given values, which must be homogenous.
151 // If the types are not all consistent (aside from elements that are of the
152 // dynamic pseudo-type) then this function will panic. It will panic also
153 // if the given list is empty, since then the element type cannot be inferred.
154 // (See also ListValEmpty.)
155 func ListVal(vals []Value) Value {
157 panic("must not call ListVal with empty slice")
159 elementType := DynamicPseudoType
160 rawList := make([]interface{}, len(vals))
162 for i, val := range vals {
163 if elementType == DynamicPseudoType {
165 } else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
167 "inconsistent list element types (%#v then %#v)",
176 ty: List(elementType),
181 // ListValEmpty returns an empty list of the given element type.
182 func ListValEmpty(element Type) Value {
189 // MapVal returns a Value of a map type whose element type is defined by
190 // the types of the given values, which must be homogenous.
192 // If the types are not all consistent (aside from elements that are of the
193 // dynamic pseudo-type) then this function will panic. It will panic also
194 // if the given map is empty, since then the element type cannot be inferred.
195 // (See also MapValEmpty.)
196 func MapVal(vals map[string]Value) Value {
198 panic("must not call MapVal with empty map")
200 elementType := DynamicPseudoType
201 rawMap := make(map[string]interface{}, len(vals))
203 for key, val := range vals {
204 if elementType == DynamicPseudoType {
206 } else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
208 "inconsistent map element types (%#v then %#v)",
213 rawMap[NormalizeString(key)] = val.v
217 ty: Map(elementType),
222 // MapValEmpty returns an empty map of the given element type.
223 func MapValEmpty(element Type) Value {
226 v: map[string]interface{}{},
230 // SetVal returns a Value of set type whose element type is defined by
231 // the types of the given values, which must be homogenous.
233 // If the types are not all consistent (aside from elements that are of the
234 // dynamic pseudo-type) then this function will panic. It will panic also
235 // if the given list is empty, since then the element type cannot be inferred.
236 // (See also SetValEmpty.)
237 func SetVal(vals []Value) Value {
239 panic("must not call SetVal with empty slice")
241 elementType := DynamicPseudoType
242 rawList := make([]interface{}, len(vals))
244 for i, val := range vals {
245 if elementType == DynamicPseudoType {
247 } else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
249 "inconsistent set element types (%#v then %#v)",
257 rawVal := set.NewSetFromSlice(setRules{elementType}, rawList)
260 ty: Set(elementType),
265 // SetValFromValueSet returns a Value of set type based on an already-constructed
268 // The element type of the returned value is the element type of the given
270 func SetValFromValueSet(s ValueSet) Value {
271 ety := s.ElementType()
272 rawVal := s.s.Copy() // copy so caller can't mutate what we wrap
280 // SetValEmpty returns an empty set of the given element type.
281 func SetValEmpty(element Type) Value {
284 v: set.NewSet(setRules{element}),
288 // CapsuleVal creates a value of the given capsule type using the given
289 // wrapVal, which must be a pointer to a value of the capsule type's native
292 // This function will panic if the given type is not a capsule type, if
293 // the given wrapVal is not compatible with the given capsule type, or if
294 // wrapVal is not a pointer.
295 func CapsuleVal(ty Type, wrapVal interface{}) Value {
296 if !ty.IsCapsuleType() {
297 panic("not a capsule type")
300 wv := reflect.ValueOf(wrapVal)
301 if wv.Kind() != reflect.Ptr {
302 panic("wrapVal is not a pointer")
305 it := ty.typeImpl.(*capsuleType).GoType
306 if !wv.Type().Elem().AssignableTo(it) {
307 panic("wrapVal target is not compatible with the given capsule type")