diff options
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/value_ops.go')
-rw-r--r-- | vendor/github.com/zclconf/go-cty/cty/value_ops.go | 1071 |
1 files changed, 1071 insertions, 0 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/value_ops.go b/vendor/github.com/zclconf/go-cty/cty/value_ops.go new file mode 100644 index 0000000..967aa76 --- /dev/null +++ b/vendor/github.com/zclconf/go-cty/cty/value_ops.go | |||
@@ -0,0 +1,1071 @@ | |||
1 | package cty | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "math/big" | ||
6 | |||
7 | "reflect" | ||
8 | |||
9 | "github.com/zclconf/go-cty/cty/set" | ||
10 | ) | ||
11 | |||
12 | func (val Value) GoString() string { | ||
13 | if val == NilVal { | ||
14 | return "cty.NilVal" | ||
15 | } | ||
16 | |||
17 | if val.ty == DynamicPseudoType { | ||
18 | return "cty.DynamicVal" | ||
19 | } | ||
20 | |||
21 | if !val.IsKnown() { | ||
22 | return fmt.Sprintf("cty.UnknownVal(%#v)", val.ty) | ||
23 | } | ||
24 | if val.IsNull() { | ||
25 | return fmt.Sprintf("cty.NullVal(%#v)", val.ty) | ||
26 | } | ||
27 | |||
28 | // By the time we reach here we've dealt with all of the exceptions around | ||
29 | // unknowns and nulls, so we're guaranteed that the values are the | ||
30 | // canonical internal representation of the given type. | ||
31 | |||
32 | switch val.ty { | ||
33 | case Bool: | ||
34 | if val.v.(bool) { | ||
35 | return "cty.True" | ||
36 | } else { | ||
37 | return "cty.False" | ||
38 | } | ||
39 | case Number: | ||
40 | fv := val.v.(*big.Float) | ||
41 | // We'll try to use NumberIntVal or NumberFloatVal if we can, since | ||
42 | // the fully-general initializer call is pretty ugly-looking. | ||
43 | if fv.IsInt() { | ||
44 | return fmt.Sprintf("cty.NumberIntVal(%#v)", fv) | ||
45 | } | ||
46 | if rfv, accuracy := fv.Float64(); accuracy == big.Exact { | ||
47 | return fmt.Sprintf("cty.NumberFloatVal(%#v)", rfv) | ||
48 | } | ||
49 | return fmt.Sprintf("cty.NumberVal(new(big.Float).Parse(\"%#v\", 10))", fv) | ||
50 | case String: | ||
51 | return fmt.Sprintf("cty.StringVal(%#v)", val.v) | ||
52 | } | ||
53 | |||
54 | switch { | ||
55 | case val.ty.IsSetType(): | ||
56 | vals := val.v.(set.Set).Values() | ||
57 | if vals == nil || len(vals) == 0 { | ||
58 | return fmt.Sprintf("cty.SetValEmpty()") | ||
59 | } else { | ||
60 | return fmt.Sprintf("cty.SetVal(%#v)", vals) | ||
61 | } | ||
62 | case val.ty.IsCapsuleType(): | ||
63 | return fmt.Sprintf("cty.CapsuleVal(%#v, %#v)", val.ty, val.v) | ||
64 | } | ||
65 | |||
66 | // Default exposes implementation details, so should actually cover | ||
67 | // all of the cases above for good caller UX. | ||
68 | return fmt.Sprintf("cty.Value{ty: %#v, v: %#v}", val.ty, val.v) | ||
69 | } | ||
70 | |||
71 | // Equals returns True if the receiver and the given other value have the | ||
72 | // same type and are exactly equal in value. | ||
73 | // | ||
74 | // The usual short-circuit rules apply, so the result can be unknown or typed | ||
75 | // as dynamic if either of the given values are. Use RawEquals to compare | ||
76 | // if two values are equal *ignoring* the short-circuit rules. | ||
77 | func (val Value) Equals(other Value) Value { | ||
78 | if val.ty.HasDynamicTypes() || other.ty.HasDynamicTypes() { | ||
79 | return UnknownVal(Bool) | ||
80 | } | ||
81 | |||
82 | if !val.ty.Equals(other.ty) { | ||
83 | return BoolVal(false) | ||
84 | } | ||
85 | |||
86 | if !(val.IsKnown() && other.IsKnown()) { | ||
87 | return UnknownVal(Bool) | ||
88 | } | ||
89 | |||
90 | if val.IsNull() || other.IsNull() { | ||
91 | if val.IsNull() && other.IsNull() { | ||
92 | return BoolVal(true) | ||
93 | } | ||
94 | return BoolVal(false) | ||
95 | } | ||
96 | |||
97 | ty := val.ty | ||
98 | result := false | ||
99 | |||
100 | switch { | ||
101 | case ty == Number: | ||
102 | result = val.v.(*big.Float).Cmp(other.v.(*big.Float)) == 0 | ||
103 | case ty == Bool: | ||
104 | result = val.v.(bool) == other.v.(bool) | ||
105 | case ty == String: | ||
106 | // Simple equality is safe because we NFC-normalize strings as they | ||
107 | // enter our world from StringVal, and so we can assume strings are | ||
108 | // always in normal form. | ||
109 | result = val.v.(string) == other.v.(string) | ||
110 | case ty.IsObjectType(): | ||
111 | oty := ty.typeImpl.(typeObject) | ||
112 | result = true | ||
113 | for attr, aty := range oty.AttrTypes { | ||
114 | lhs := Value{ | ||
115 | ty: aty, | ||
116 | v: val.v.(map[string]interface{})[attr], | ||
117 | } | ||
118 | rhs := Value{ | ||
119 | ty: aty, | ||
120 | v: other.v.(map[string]interface{})[attr], | ||
121 | } | ||
122 | eq := lhs.Equals(rhs) | ||
123 | if !eq.IsKnown() { | ||
124 | return UnknownVal(Bool) | ||
125 | } | ||
126 | if eq.False() { | ||
127 | result = false | ||
128 | break | ||
129 | } | ||
130 | } | ||
131 | case ty.IsTupleType(): | ||
132 | tty := ty.typeImpl.(typeTuple) | ||
133 | result = true | ||
134 | for i, ety := range tty.ElemTypes { | ||
135 | lhs := Value{ | ||
136 | ty: ety, | ||
137 | v: val.v.([]interface{})[i], | ||
138 | } | ||
139 | rhs := Value{ | ||
140 | ty: ety, | ||
141 | v: other.v.([]interface{})[i], | ||
142 | } | ||
143 | eq := lhs.Equals(rhs) | ||
144 | if !eq.IsKnown() { | ||
145 | return UnknownVal(Bool) | ||
146 | } | ||
147 | if eq.False() { | ||
148 | result = false | ||
149 | break | ||
150 | } | ||
151 | } | ||
152 | case ty.IsListType(): | ||
153 | ety := ty.typeImpl.(typeList).ElementTypeT | ||
154 | if len(val.v.([]interface{})) == len(other.v.([]interface{})) { | ||
155 | result = true | ||
156 | for i := range val.v.([]interface{}) { | ||
157 | lhs := Value{ | ||
158 | ty: ety, | ||
159 | v: val.v.([]interface{})[i], | ||
160 | } | ||
161 | rhs := Value{ | ||
162 | ty: ety, | ||
163 | v: other.v.([]interface{})[i], | ||
164 | } | ||
165 | eq := lhs.Equals(rhs) | ||
166 | if !eq.IsKnown() { | ||
167 | return UnknownVal(Bool) | ||
168 | } | ||
169 | if eq.False() { | ||
170 | result = false | ||
171 | break | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | case ty.IsSetType(): | ||
176 | s1 := val.v.(set.Set) | ||
177 | s2 := other.v.(set.Set) | ||
178 | equal := true | ||
179 | |||
180 | // Note that by our definition of sets it's never possible for two | ||
181 | // sets that contain unknown values (directly or indicrectly) to | ||
182 | // ever be equal, even if they are otherwise identical. | ||
183 | |||
184 | // FIXME: iterating both lists and checking each item is not the | ||
185 | // ideal implementation here, but it works with the primitives we | ||
186 | // have in the set implementation. Perhaps the set implementation | ||
187 | // can provide its own equality test later. | ||
188 | s1.EachValue(func(v interface{}) { | ||
189 | if !s2.Has(v) { | ||
190 | equal = false | ||
191 | } | ||
192 | }) | ||
193 | s2.EachValue(func(v interface{}) { | ||
194 | if !s1.Has(v) { | ||
195 | equal = false | ||
196 | } | ||
197 | }) | ||
198 | |||
199 | result = equal | ||
200 | case ty.IsMapType(): | ||
201 | ety := ty.typeImpl.(typeMap).ElementTypeT | ||
202 | if len(val.v.(map[string]interface{})) == len(other.v.(map[string]interface{})) { | ||
203 | result = true | ||
204 | for k := range val.v.(map[string]interface{}) { | ||
205 | if _, ok := other.v.(map[string]interface{})[k]; !ok { | ||
206 | result = false | ||
207 | break | ||
208 | } | ||
209 | lhs := Value{ | ||
210 | ty: ety, | ||
211 | v: val.v.(map[string]interface{})[k], | ||
212 | } | ||
213 | rhs := Value{ | ||
214 | ty: ety, | ||
215 | v: other.v.(map[string]interface{})[k], | ||
216 | } | ||
217 | eq := lhs.Equals(rhs) | ||
218 | if !eq.IsKnown() { | ||
219 | return UnknownVal(Bool) | ||
220 | } | ||
221 | if eq.False() { | ||
222 | result = false | ||
223 | break | ||
224 | } | ||
225 | } | ||
226 | } | ||
227 | case ty.IsCapsuleType(): | ||
228 | // A capsule type's encapsulated value is a pointer to a value of its | ||
229 | // native type, so we can just compare these to get the identity test | ||
230 | // we need. | ||
231 | return BoolVal(val.v == other.v) | ||
232 | |||
233 | default: | ||
234 | // should never happen | ||
235 | panic(fmt.Errorf("unsupported value type %#v in Equals", ty)) | ||
236 | } | ||
237 | |||
238 | return BoolVal(result) | ||
239 | } | ||
240 | |||
241 | // NotEqual is a shorthand for Equals followed by Not. | ||
242 | func (val Value) NotEqual(other Value) Value { | ||
243 | return val.Equals(other).Not() | ||
244 | } | ||
245 | |||
246 | // True returns true if the receiver is True, false if False, and panics if | ||
247 | // the receiver is not of type Bool. | ||
248 | // | ||
249 | // This is a helper function to help write application logic that works with | ||
250 | // values, rather than a first-class operation. It does not work with unknown | ||
251 | // or null values. For more robust handling with unknown value | ||
252 | // short-circuiting, use val.Equals(cty.True). | ||
253 | func (val Value) True() bool { | ||
254 | if val.ty != Bool { | ||
255 | panic("not bool") | ||
256 | } | ||
257 | return val.Equals(True).v.(bool) | ||
258 | } | ||
259 | |||
260 | // False is the opposite of True. | ||
261 | func (val Value) False() bool { | ||
262 | return !val.True() | ||
263 | } | ||
264 | |||
265 | // RawEquals returns true if and only if the two given values have the same | ||
266 | // type and equal value, ignoring the usual short-circuit rules about | ||
267 | // unknowns and dynamic types. | ||
268 | // | ||
269 | // This method is more appropriate for testing than for real use, since it | ||
270 | // skips over usual semantics around unknowns but as a consequence allows | ||
271 | // testing the result of another operation that is expected to return unknown. | ||
272 | // It returns a primitive Go bool rather than a Value to remind us that it | ||
273 | // is not a first-class value operation. | ||
274 | func (val Value) RawEquals(other Value) bool { | ||
275 | if !val.ty.Equals(other.ty) { | ||
276 | return false | ||
277 | } | ||
278 | if (!val.IsKnown()) && (!other.IsKnown()) { | ||
279 | return true | ||
280 | } | ||
281 | if (val.IsKnown() && !other.IsKnown()) || (other.IsKnown() && !val.IsKnown()) { | ||
282 | return false | ||
283 | } | ||
284 | if val.IsNull() && other.IsNull() { | ||
285 | return true | ||
286 | } | ||
287 | if (val.IsNull() && !other.IsNull()) || (other.IsNull() && !val.IsNull()) { | ||
288 | return false | ||
289 | } | ||
290 | if val.ty == DynamicPseudoType && other.ty == DynamicPseudoType { | ||
291 | return true | ||
292 | } | ||
293 | |||
294 | ty := val.ty | ||
295 | switch { | ||
296 | case ty == Number || ty == Bool || ty == String || ty == DynamicPseudoType: | ||
297 | return val.Equals(other).True() | ||
298 | case ty.IsObjectType(): | ||
299 | oty := ty.typeImpl.(typeObject) | ||
300 | for attr, aty := range oty.AttrTypes { | ||
301 | lhs := Value{ | ||
302 | ty: aty, | ||
303 | v: val.v.(map[string]interface{})[attr], | ||
304 | } | ||
305 | rhs := Value{ | ||
306 | ty: aty, | ||
307 | v: other.v.(map[string]interface{})[attr], | ||
308 | } | ||
309 | eq := lhs.RawEquals(rhs) | ||
310 | if !eq { | ||
311 | return false | ||
312 | } | ||
313 | } | ||
314 | return true | ||
315 | case ty.IsTupleType(): | ||
316 | tty := ty.typeImpl.(typeTuple) | ||
317 | for i, ety := range tty.ElemTypes { | ||
318 | lhs := Value{ | ||
319 | ty: ety, | ||
320 | v: val.v.([]interface{})[i], | ||
321 | } | ||
322 | rhs := Value{ | ||
323 | ty: ety, | ||
324 | v: other.v.([]interface{})[i], | ||
325 | } | ||
326 | eq := lhs.RawEquals(rhs) | ||
327 | if !eq { | ||
328 | return false | ||
329 | } | ||
330 | } | ||
331 | return true | ||
332 | case ty.IsListType(): | ||
333 | ety := ty.typeImpl.(typeList).ElementTypeT | ||
334 | if len(val.v.([]interface{})) == len(other.v.([]interface{})) { | ||
335 | for i := range val.v.([]interface{}) { | ||
336 | lhs := Value{ | ||
337 | ty: ety, | ||
338 | v: val.v.([]interface{})[i], | ||
339 | } | ||
340 | rhs := Value{ | ||
341 | ty: ety, | ||
342 | v: other.v.([]interface{})[i], | ||
343 | } | ||
344 | eq := lhs.RawEquals(rhs) | ||
345 | if !eq { | ||
346 | return false | ||
347 | } | ||
348 | } | ||
349 | return true | ||
350 | } | ||
351 | return false | ||
352 | case ty.IsSetType(): | ||
353 | s1 := val.v.(set.Set) | ||
354 | s2 := other.v.(set.Set) | ||
355 | |||
356 | // Since we're intentionally ignoring our rule that two unknowns | ||
357 | // are never equal, we can cheat here. | ||
358 | // (This isn't 100% right since e.g. it will fail if the set contains | ||
359 | // numbers that are infinite, which DeepEqual can't compare properly. | ||
360 | // We're accepting that limitation for simplicity here, since this | ||
361 | // function is here primarily for testing.) | ||
362 | return reflect.DeepEqual(s1, s2) | ||
363 | |||
364 | case ty.IsMapType(): | ||
365 | ety := ty.typeImpl.(typeMap).ElementTypeT | ||
366 | if len(val.v.(map[string]interface{})) == len(other.v.(map[string]interface{})) { | ||
367 | for k := range val.v.(map[string]interface{}) { | ||
368 | if _, ok := other.v.(map[string]interface{})[k]; !ok { | ||
369 | return false | ||
370 | } | ||
371 | lhs := Value{ | ||
372 | ty: ety, | ||
373 | v: val.v.(map[string]interface{})[k], | ||
374 | } | ||
375 | rhs := Value{ | ||
376 | ty: ety, | ||
377 | v: other.v.(map[string]interface{})[k], | ||
378 | } | ||
379 | eq := lhs.RawEquals(rhs) | ||
380 | if !eq { | ||
381 | return false | ||
382 | } | ||
383 | } | ||
384 | return true | ||
385 | } | ||
386 | return false | ||
387 | case ty.IsCapsuleType(): | ||
388 | // A capsule type's encapsulated value is a pointer to a value of its | ||
389 | // native type, so we can just compare these to get the identity test | ||
390 | // we need. | ||
391 | return val.v == other.v | ||
392 | |||
393 | default: | ||
394 | // should never happen | ||
395 | panic(fmt.Errorf("unsupported value type %#v in RawEquals", ty)) | ||
396 | } | ||
397 | } | ||
398 | |||
399 | // Add returns the sum of the receiver and the given other value. Both values | ||
400 | // must be numbers; this method will panic if not. | ||
401 | func (val Value) Add(other Value) Value { | ||
402 | if shortCircuit := mustTypeCheck(Number, Number, val, other); shortCircuit != nil { | ||
403 | shortCircuit = forceShortCircuitType(shortCircuit, Number) | ||
404 | return *shortCircuit | ||
405 | } | ||
406 | |||
407 | ret := new(big.Float) | ||
408 | ret.Add(val.v.(*big.Float), other.v.(*big.Float)) | ||
409 | return NumberVal(ret) | ||
410 | } | ||
411 | |||
412 | // Subtract returns receiver minus the given other value. Both values must be | ||
413 | // numbers; this method will panic if not. | ||
414 | func (val Value) Subtract(other Value) Value { | ||
415 | if shortCircuit := mustTypeCheck(Number, Number, val, other); shortCircuit != nil { | ||
416 | shortCircuit = forceShortCircuitType(shortCircuit, Number) | ||
417 | return *shortCircuit | ||
418 | } | ||
419 | |||
420 | return val.Add(other.Negate()) | ||
421 | } | ||
422 | |||
423 | // Negate returns the numeric negative of the receiver, which must be a number. | ||
424 | // This method will panic when given a value of any other type. | ||
425 | func (val Value) Negate() Value { | ||
426 | if shortCircuit := mustTypeCheck(Number, Number, val); shortCircuit != nil { | ||
427 | shortCircuit = forceShortCircuitType(shortCircuit, Number) | ||
428 | return *shortCircuit | ||
429 | } | ||
430 | |||
431 | ret := new(big.Float).Neg(val.v.(*big.Float)) | ||
432 | return NumberVal(ret) | ||
433 | } | ||
434 | |||
435 | // Multiply returns the product of the receiver and the given other value. | ||
436 | // Both values must be numbers; this method will panic if not. | ||
437 | func (val Value) Multiply(other Value) Value { | ||
438 | if shortCircuit := mustTypeCheck(Number, Number, val, other); shortCircuit != nil { | ||
439 | shortCircuit = forceShortCircuitType(shortCircuit, Number) | ||
440 | return *shortCircuit | ||
441 | } | ||
442 | |||
443 | ret := new(big.Float) | ||
444 | ret.Mul(val.v.(*big.Float), other.v.(*big.Float)) | ||
445 | return NumberVal(ret) | ||
446 | } | ||
447 | |||
448 | // Divide returns the quotient of the receiver and the given other value. | ||
449 | // Both values must be numbers; this method will panic if not. | ||
450 | // | ||
451 | // If the "other" value is exactly zero, this operation will return either | ||
452 | // PositiveInfinity or NegativeInfinity, depending on the sign of the | ||
453 | // receiver value. For some use-cases the presence of infinities may be | ||
454 | // undesirable, in which case the caller should check whether the | ||
455 | // other value equals zero before calling and raise an error instead. | ||
456 | // | ||
457 | // If both values are zero or infinity, this function will panic with | ||
458 | // an instance of big.ErrNaN. | ||
459 | func (val Value) Divide(other Value) Value { | ||
460 | if shortCircuit := mustTypeCheck(Number, Number, val, other); shortCircuit != nil { | ||
461 | shortCircuit = forceShortCircuitType(shortCircuit, Number) | ||
462 | return *shortCircuit | ||
463 | } | ||
464 | |||
465 | ret := new(big.Float) | ||
466 | ret.Quo(val.v.(*big.Float), other.v.(*big.Float)) | ||
467 | return NumberVal(ret) | ||
468 | } | ||
469 | |||
470 | // Modulo returns the remainder of an integer division of the receiver and | ||
471 | // the given other value. Both values must be numbers; this method will panic | ||
472 | // if not. | ||
473 | // | ||
474 | // If the "other" value is exactly zero, this operation will return either | ||
475 | // PositiveInfinity or NegativeInfinity, depending on the sign of the | ||
476 | // receiver value. For some use-cases the presence of infinities may be | ||
477 | // undesirable, in which case the caller should check whether the | ||
478 | // other value equals zero before calling and raise an error instead. | ||
479 | // | ||
480 | // This operation is primarily here for use with nonzero natural numbers. | ||
481 | // Modulo with "other" as a non-natural number gets somewhat philosophical, | ||
482 | // and this function takes a position on what that should mean, but callers | ||
483 | // may wish to disallow such things outright or implement their own modulo | ||
484 | // if they disagree with the interpretation used here. | ||
485 | func (val Value) Modulo(other Value) Value { | ||
486 | if shortCircuit := mustTypeCheck(Number, Number, val, other); shortCircuit != nil { | ||
487 | shortCircuit = forceShortCircuitType(shortCircuit, Number) | ||
488 | return *shortCircuit | ||
489 | } | ||
490 | |||
491 | // We cheat a bit here with infinities, just abusing the Multiply operation | ||
492 | // to get an infinite result of the correct sign. | ||
493 | if val == PositiveInfinity || val == NegativeInfinity || other == PositiveInfinity || other == NegativeInfinity { | ||
494 | return val.Multiply(other) | ||
495 | } | ||
496 | |||
497 | if other.RawEquals(Zero) { | ||
498 | return val | ||
499 | } | ||
500 | |||
501 | // FIXME: This is a bit clumsy. Should come back later and see if there's a | ||
502 | // more straightforward way to do this. | ||
503 | rat := val.Divide(other) | ||
504 | ratFloorInt := &big.Int{} | ||
505 | rat.v.(*big.Float).Int(ratFloorInt) | ||
506 | work := (&big.Float{}).SetInt(ratFloorInt) | ||
507 | work.Mul(other.v.(*big.Float), work) | ||
508 | work.Sub(val.v.(*big.Float), work) | ||
509 | |||
510 | return NumberVal(work) | ||
511 | } | ||
512 | |||
513 | // Absolute returns the absolute (signless) value of the receiver, which must | ||
514 | // be a number or this method will panic. | ||
515 | func (val Value) Absolute() Value { | ||
516 | if shortCircuit := mustTypeCheck(Number, Number, val); shortCircuit != nil { | ||
517 | shortCircuit = forceShortCircuitType(shortCircuit, Number) | ||
518 | return *shortCircuit | ||
519 | } | ||
520 | |||
521 | ret := (&big.Float{}).Abs(val.v.(*big.Float)) | ||
522 | return NumberVal(ret) | ||
523 | } | ||
524 | |||
525 | // GetAttr returns the value of the given attribute of the receiver, which | ||
526 | // must be of an object type that has an attribute of the given name. | ||
527 | // This method will panic if the receiver type is not compatible. | ||
528 | // | ||
529 | // The method will also panic if the given attribute name is not defined | ||
530 | // for the value's type. Use the attribute-related methods on Type to | ||
531 | // check for the validity of an attribute before trying to use it. | ||
532 | // | ||
533 | // This method may be called on a value whose type is DynamicPseudoType, | ||
534 | // in which case the result will also be DynamicVal. | ||
535 | func (val Value) GetAttr(name string) Value { | ||
536 | if val.ty == DynamicPseudoType { | ||
537 | return DynamicVal | ||
538 | } | ||
539 | |||
540 | if !val.ty.IsObjectType() { | ||
541 | panic("value is not an object") | ||
542 | } | ||
543 | |||
544 | name = NormalizeString(name) | ||
545 | if !val.ty.HasAttribute(name) { | ||
546 | panic("value has no attribute of that name") | ||
547 | } | ||
548 | |||
549 | attrType := val.ty.AttributeType(name) | ||
550 | |||
551 | if !val.IsKnown() { | ||
552 | return UnknownVal(attrType) | ||
553 | } | ||
554 | |||
555 | return Value{ | ||
556 | ty: attrType, | ||
557 | v: val.v.(map[string]interface{})[name], | ||
558 | } | ||
559 | } | ||
560 | |||
561 | // Index returns the value of an element of the receiver, which must have | ||
562 | // either a list, map or tuple type. This method will panic if the receiver | ||
563 | // type is not compatible. | ||
564 | // | ||
565 | // The key value must be the correct type for the receving collection: a | ||
566 | // number if the collection is a list or tuple, or a string if it is a map. | ||
567 | // In the case of a list or tuple, the given number must be convertable to int | ||
568 | // or this method will panic. The key may alternatively be of | ||
569 | // DynamicPseudoType, in which case the result itself is an unknown of the | ||
570 | // collection's element type. | ||
571 | // | ||
572 | // The result is of the receiver collection's element type, or in the case | ||
573 | // of a tuple the type of the specific element index requested. | ||
574 | // | ||
575 | // This method may be called on a value whose type is DynamicPseudoType, | ||
576 | // in which case the result will also be the DynamicValue. | ||
577 | func (val Value) Index(key Value) Value { | ||
578 | if val.ty == DynamicPseudoType { | ||
579 | return DynamicVal | ||
580 | } | ||
581 | |||
582 | switch { | ||
583 | case val.Type().IsListType(): | ||
584 | elty := val.Type().ElementType() | ||
585 | if key.Type() == DynamicPseudoType { | ||
586 | return UnknownVal(elty) | ||
587 | } | ||
588 | |||
589 | if key.Type() != Number { | ||
590 | panic("element key for list must be number") | ||
591 | } | ||
592 | if !key.IsKnown() { | ||
593 | return UnknownVal(elty) | ||
594 | } | ||
595 | |||
596 | if !val.IsKnown() { | ||
597 | return UnknownVal(elty) | ||
598 | } | ||
599 | |||
600 | index, accuracy := key.v.(*big.Float).Int64() | ||
601 | if accuracy != big.Exact || index < 0 { | ||
602 | panic("element key for list must be non-negative integer") | ||
603 | } | ||
604 | |||
605 | return Value{ | ||
606 | ty: elty, | ||
607 | v: val.v.([]interface{})[index], | ||
608 | } | ||
609 | case val.Type().IsMapType(): | ||
610 | elty := val.Type().ElementType() | ||
611 | if key.Type() == DynamicPseudoType { | ||
612 | return UnknownVal(elty) | ||
613 | } | ||
614 | |||
615 | if key.Type() != String { | ||
616 | panic("element key for map must be string") | ||
617 | } | ||
618 | if !key.IsKnown() { | ||
619 | return UnknownVal(elty) | ||
620 | } | ||
621 | |||
622 | if !val.IsKnown() { | ||
623 | return UnknownVal(elty) | ||
624 | } | ||
625 | |||
626 | keyStr := key.v.(string) | ||
627 | |||
628 | return Value{ | ||
629 | ty: elty, | ||
630 | v: val.v.(map[string]interface{})[keyStr], | ||
631 | } | ||
632 | case val.Type().IsTupleType(): | ||
633 | if key.Type() == DynamicPseudoType { | ||
634 | return DynamicVal | ||
635 | } | ||
636 | |||
637 | if key.Type() != Number { | ||
638 | panic("element key for tuple must be number") | ||
639 | } | ||
640 | if !key.IsKnown() { | ||
641 | return DynamicVal | ||
642 | } | ||
643 | |||
644 | index, accuracy := key.v.(*big.Float).Int64() | ||
645 | if accuracy != big.Exact || index < 0 { | ||
646 | panic("element key for list must be non-negative integer") | ||
647 | } | ||
648 | |||
649 | eltys := val.Type().TupleElementTypes() | ||
650 | |||
651 | if !val.IsKnown() { | ||
652 | return UnknownVal(eltys[index]) | ||
653 | } | ||
654 | |||
655 | return Value{ | ||
656 | ty: eltys[index], | ||
657 | v: val.v.([]interface{})[index], | ||
658 | } | ||
659 | default: | ||
660 | panic("not a list, map, or tuple type") | ||
661 | } | ||
662 | } | ||
663 | |||
664 | // HasIndex returns True if the receiver (which must be supported for Index) | ||
665 | // has an element with the given index key, or False if it does not. | ||
666 | // | ||
667 | // The result will be UnknownVal(Bool) if either the collection or the | ||
668 | // key value are unknown. | ||
669 | // | ||
670 | // This method will panic if the receiver is not indexable, but does not | ||
671 | // impose any panic-causing type constraints on the key. | ||
672 | func (val Value) HasIndex(key Value) Value { | ||
673 | if val.ty == DynamicPseudoType { | ||
674 | return UnknownVal(Bool) | ||
675 | } | ||
676 | |||
677 | switch { | ||
678 | case val.Type().IsListType(): | ||
679 | if key.Type() == DynamicPseudoType { | ||
680 | return UnknownVal(Bool) | ||
681 | } | ||
682 | |||
683 | if key.Type() != Number { | ||
684 | return False | ||
685 | } | ||
686 | if !key.IsKnown() { | ||
687 | return UnknownVal(Bool) | ||
688 | } | ||
689 | if !val.IsKnown() { | ||
690 | return UnknownVal(Bool) | ||
691 | } | ||
692 | |||
693 | index, accuracy := key.v.(*big.Float).Int64() | ||
694 | if accuracy != big.Exact || index < 0 { | ||
695 | return False | ||
696 | } | ||
697 | |||
698 | return BoolVal(int(index) < len(val.v.([]interface{})) && index >= 0) | ||
699 | case val.Type().IsMapType(): | ||
700 | if key.Type() == DynamicPseudoType { | ||
701 | return UnknownVal(Bool) | ||
702 | } | ||
703 | |||
704 | if key.Type() != String { | ||
705 | return False | ||
706 | } | ||
707 | if !key.IsKnown() { | ||
708 | return UnknownVal(Bool) | ||
709 | } | ||
710 | if !val.IsKnown() { | ||
711 | return UnknownVal(Bool) | ||
712 | } | ||
713 | |||
714 | keyStr := key.v.(string) | ||
715 | _, exists := val.v.(map[string]interface{})[keyStr] | ||
716 | |||
717 | return BoolVal(exists) | ||
718 | case val.Type().IsTupleType(): | ||
719 | if key.Type() == DynamicPseudoType { | ||
720 | return UnknownVal(Bool) | ||
721 | } | ||
722 | |||
723 | if key.Type() != Number { | ||
724 | return False | ||
725 | } | ||
726 | if !key.IsKnown() { | ||
727 | return UnknownVal(Bool) | ||
728 | } | ||
729 | |||
730 | index, accuracy := key.v.(*big.Float).Int64() | ||
731 | if accuracy != big.Exact || index < 0 { | ||
732 | return False | ||
733 | } | ||
734 | |||
735 | length := val.Type().Length() | ||
736 | return BoolVal(int(index) < length && index >= 0) | ||
737 | default: | ||
738 | panic("not a list, map, or tuple type") | ||
739 | } | ||
740 | } | ||
741 | |||
742 | // HasElement returns True if the receiver (which must be of a set type) | ||
743 | // has the given value as an element, or False if it does not. | ||
744 | // | ||
745 | // The result will be UnknownVal(Bool) if either the set or the | ||
746 | // given value are unknown. | ||
747 | // | ||
748 | // This method will panic if the receiver is not a set, or if it is a null set. | ||
749 | func (val Value) HasElement(elem Value) Value { | ||
750 | ty := val.Type() | ||
751 | |||
752 | if !ty.IsSetType() { | ||
753 | panic("not a set type") | ||
754 | } | ||
755 | if !val.IsKnown() || !elem.IsKnown() { | ||
756 | return UnknownVal(Bool) | ||
757 | } | ||
758 | if val.IsNull() { | ||
759 | panic("can't call HasElement on a nil value") | ||
760 | } | ||
761 | if ty.ElementType() != elem.Type() { | ||
762 | return False | ||
763 | } | ||
764 | |||
765 | s := val.v.(set.Set) | ||
766 | return BoolVal(s.Has(elem.v)) | ||
767 | } | ||
768 | |||
769 | // Length returns the length of the receiver, which must be a collection type | ||
770 | // or tuple type, as a number value. If the receiver is not a compatible type | ||
771 | // then this method will panic. | ||
772 | // | ||
773 | // If the receiver is unknown then the result is also unknown. | ||
774 | // | ||
775 | // If the receiver is null then this function will panic. | ||
776 | // | ||
777 | // Note that Length is not supported for strings. To determine the length | ||
778 | // of a string, call AsString and take the length of the native Go string | ||
779 | // that is returned. | ||
780 | func (val Value) Length() Value { | ||
781 | if val.Type().IsTupleType() { | ||
782 | // For tuples, we can return the length even if the value is not known. | ||
783 | return NumberIntVal(int64(val.Type().Length())) | ||
784 | } | ||
785 | |||
786 | if !val.IsKnown() { | ||
787 | return UnknownVal(Number) | ||
788 | } | ||
789 | |||
790 | return NumberIntVal(int64(val.LengthInt())) | ||
791 | } | ||
792 | |||
793 | // LengthInt is like Length except it returns an int. It has the same behavior | ||
794 | // as Length except that it will panic if the receiver is unknown. | ||
795 | // | ||
796 | // This is an integration method provided for the convenience of code bridging | ||
797 | // into Go's type system. | ||
798 | func (val Value) LengthInt() int { | ||
799 | if val.Type().IsTupleType() { | ||
800 | // For tuples, we can return the length even if the value is not known. | ||
801 | return val.Type().Length() | ||
802 | } | ||
803 | if !val.IsKnown() { | ||
804 | panic("value is not known") | ||
805 | } | ||
806 | if val.IsNull() { | ||
807 | panic("value is null") | ||
808 | } | ||
809 | |||
810 | switch { | ||
811 | |||
812 | case val.ty.IsListType(): | ||
813 | return len(val.v.([]interface{})) | ||
814 | |||
815 | case val.ty.IsSetType(): | ||
816 | return val.v.(set.Set).Length() | ||
817 | |||
818 | case val.ty.IsMapType(): | ||
819 | return len(val.v.(map[string]interface{})) | ||
820 | |||
821 | default: | ||
822 | panic("value is not a collection") | ||
823 | } | ||
824 | } | ||
825 | |||
826 | // ElementIterator returns an ElementIterator for iterating the elements | ||
827 | // of the receiver, which must be a collection type, a tuple type, or an object | ||
828 | // type. If called on a method of any other type, this method will panic. | ||
829 | // | ||
830 | // The value must be Known and non-Null, or this method will panic. | ||
831 | // | ||
832 | // If the receiver is of a list type, the returned keys will be of type Number | ||
833 | // and the values will be of the list's element type. | ||
834 | // | ||
835 | // If the receiver is of a map type, the returned keys will be of type String | ||
836 | // and the value will be of the map's element type. Elements are passed in | ||
837 | // ascending lexicographical order by key. | ||
838 | // | ||
839 | // If the receiver is of a set type, each element is returned as both the | ||
840 | // key and the value, since set members are their own identity. | ||
841 | // | ||
842 | // If the receiver is of a tuple type, the returned keys will be of type Number | ||
843 | // and the value will be of the corresponding element's type. | ||
844 | // | ||
845 | // If the receiver is of an object type, the returned keys will be of type | ||
846 | // String and the value will be of the corresponding attributes's type. | ||
847 | // | ||
848 | // ElementIterator is an integration method, so it cannot handle Unknown | ||
849 | // values. This method will panic if the receiver is Unknown. | ||
850 | func (val Value) ElementIterator() ElementIterator { | ||
851 | if !val.IsKnown() { | ||
852 | panic("can't use ElementIterator on unknown value") | ||
853 | } | ||
854 | if val.IsNull() { | ||
855 | panic("can't use ElementIterator on null value") | ||
856 | } | ||
857 | return elementIterator(val) | ||
858 | } | ||
859 | |||
860 | // CanIterateElements returns true if the receiver can support the | ||
861 | // ElementIterator method (and by extension, ForEachElement) without panic. | ||
862 | func (val Value) CanIterateElements() bool { | ||
863 | return canElementIterator(val) | ||
864 | } | ||
865 | |||
866 | // ForEachElement executes a given callback function for each element of | ||
867 | // the receiver, which must be a collection type or tuple type, or this method | ||
868 | // will panic. | ||
869 | // | ||
870 | // ForEachElement uses ElementIterator internally, and so the values passed | ||
871 | // to the callback are as described for ElementIterator. | ||
872 | // | ||
873 | // Returns true if the iteration exited early due to the callback function | ||
874 | // returning true, or false if the loop ran to completion. | ||
875 | // | ||
876 | // ForEachElement is an integration method, so it cannot handle Unknown | ||
877 | // values. This method will panic if the receiver is Unknown. | ||
878 | func (val Value) ForEachElement(cb ElementCallback) bool { | ||
879 | it := val.ElementIterator() | ||
880 | for it.Next() { | ||
881 | key, val := it.Element() | ||
882 | stop := cb(key, val) | ||
883 | if stop { | ||
884 | return true | ||
885 | } | ||
886 | } | ||
887 | return false | ||
888 | } | ||
889 | |||
890 | // Not returns the logical inverse of the receiver, which must be of type | ||
891 | // Bool or this method will panic. | ||
892 | func (val Value) Not() Value { | ||
893 | if shortCircuit := mustTypeCheck(Bool, Bool, val); shortCircuit != nil { | ||
894 | shortCircuit = forceShortCircuitType(shortCircuit, Bool) | ||
895 | return *shortCircuit | ||
896 | } | ||
897 | |||
898 | return BoolVal(!val.v.(bool)) | ||
899 | } | ||
900 | |||
901 | // And returns the result of logical AND with the receiver and the other given | ||
902 | // value, which must both be of type Bool or this method will panic. | ||
903 | func (val Value) And(other Value) Value { | ||
904 | if shortCircuit := mustTypeCheck(Bool, Bool, val, other); shortCircuit != nil { | ||
905 | shortCircuit = forceShortCircuitType(shortCircuit, Bool) | ||
906 | return *shortCircuit | ||
907 | } | ||
908 | |||
909 | return BoolVal(val.v.(bool) && other.v.(bool)) | ||
910 | } | ||
911 | |||
912 | // Or returns the result of logical OR with the receiver and the other given | ||
913 | // value, which must both be of type Bool or this method will panic. | ||
914 | func (val Value) Or(other Value) Value { | ||
915 | if shortCircuit := mustTypeCheck(Bool, Bool, val, other); shortCircuit != nil { | ||
916 | shortCircuit = forceShortCircuitType(shortCircuit, Bool) | ||
917 | return *shortCircuit | ||
918 | } | ||
919 | |||
920 | return BoolVal(val.v.(bool) || other.v.(bool)) | ||
921 | } | ||
922 | |||
923 | // LessThan returns True if the receiver is less than the other given value, | ||
924 | // which must both be numbers or this method will panic. | ||
925 | func (val Value) LessThan(other Value) Value { | ||
926 | if shortCircuit := mustTypeCheck(Number, Bool, val, other); shortCircuit != nil { | ||
927 | shortCircuit = forceShortCircuitType(shortCircuit, Bool) | ||
928 | return *shortCircuit | ||
929 | } | ||
930 | |||
931 | return BoolVal(val.v.(*big.Float).Cmp(other.v.(*big.Float)) < 0) | ||
932 | } | ||
933 | |||
934 | // GreaterThan returns True if the receiver is greater than the other given | ||
935 | // value, which must both be numbers or this method will panic. | ||
936 | func (val Value) GreaterThan(other Value) Value { | ||
937 | if shortCircuit := mustTypeCheck(Number, Bool, val, other); shortCircuit != nil { | ||
938 | shortCircuit = forceShortCircuitType(shortCircuit, Bool) | ||
939 | return *shortCircuit | ||
940 | } | ||
941 | |||
942 | return BoolVal(val.v.(*big.Float).Cmp(other.v.(*big.Float)) > 0) | ||
943 | } | ||
944 | |||
945 | // LessThanOrEqualTo is equivalent to LessThan and Equal combined with Or. | ||
946 | func (val Value) LessThanOrEqualTo(other Value) Value { | ||
947 | return val.LessThan(other).Or(val.Equals(other)) | ||
948 | } | ||
949 | |||
950 | // GreaterThanOrEqualTo is equivalent to GreaterThan and Equal combined with Or. | ||
951 | func (val Value) GreaterThanOrEqualTo(other Value) Value { | ||
952 | return val.GreaterThan(other).Or(val.Equals(other)) | ||
953 | } | ||
954 | |||
955 | // AsString returns the native string from a non-null, non-unknown cty.String | ||
956 | // value, or panics if called on any other value. | ||
957 | func (val Value) AsString() string { | ||
958 | if val.ty != String { | ||
959 | panic("not a string") | ||
960 | } | ||
961 | if val.IsNull() { | ||
962 | panic("value is null") | ||
963 | } | ||
964 | if !val.IsKnown() { | ||
965 | panic("value is unknown") | ||
966 | } | ||
967 | |||
968 | return val.v.(string) | ||
969 | } | ||
970 | |||
971 | // AsBigFloat returns a big.Float representation of a non-null, non-unknown | ||
972 | // cty.Number value, or panics if called on any other value. | ||
973 | // | ||
974 | // For more convenient conversions to other native numeric types, use the | ||
975 | // "gocty" package. | ||
976 | func (val Value) AsBigFloat() *big.Float { | ||
977 | if val.ty != Number { | ||
978 | panic("not a number") | ||
979 | } | ||
980 | if val.IsNull() { | ||
981 | panic("value is null") | ||
982 | } | ||
983 | if !val.IsKnown() { | ||
984 | panic("value is unknown") | ||
985 | } | ||
986 | |||
987 | // Copy the float so that callers can't mutate our internal state | ||
988 | ret := *(val.v.(*big.Float)) | ||
989 | |||
990 | return &ret | ||
991 | } | ||
992 | |||
993 | // AsValueSlice returns a []cty.Value representation of a non-null, non-unknown | ||
994 | // value of any type that CanIterateElements, or panics if called on | ||
995 | // any other value. | ||
996 | // | ||
997 | // For more convenient conversions to slices of more specific types, use | ||
998 | // the "gocty" package. | ||
999 | func (val Value) AsValueSlice() []Value { | ||
1000 | l := val.LengthInt() | ||
1001 | if l == 0 { | ||
1002 | return nil | ||
1003 | } | ||
1004 | |||
1005 | ret := make([]Value, 0, l) | ||
1006 | for it := val.ElementIterator(); it.Next(); { | ||
1007 | _, v := it.Element() | ||
1008 | ret = append(ret, v) | ||
1009 | } | ||
1010 | return ret | ||
1011 | } | ||
1012 | |||
1013 | // AsValueMap returns a map[string]cty.Value representation of a non-null, | ||
1014 | // non-unknown value of any type that CanIterateElements, or panics if called | ||
1015 | // on any other value. | ||
1016 | // | ||
1017 | // For more convenient conversions to maps of more specific types, use | ||
1018 | // the "gocty" package. | ||
1019 | func (val Value) AsValueMap() map[string]Value { | ||
1020 | l := val.LengthInt() | ||
1021 | if l == 0 { | ||
1022 | return nil | ||
1023 | } | ||
1024 | |||
1025 | ret := make(map[string]Value, l) | ||
1026 | for it := val.ElementIterator(); it.Next(); { | ||
1027 | k, v := it.Element() | ||
1028 | ret[k.AsString()] = v | ||
1029 | } | ||
1030 | return ret | ||
1031 | } | ||
1032 | |||
1033 | // AsValueSet returns a ValueSet representation of a non-null, | ||
1034 | // non-unknown value of any collection type, or panics if called | ||
1035 | // on any other value. | ||
1036 | // | ||
1037 | // Unlike AsValueSlice and AsValueMap, this method requires specifically a | ||
1038 | // collection type (list, set or map) and does not allow structural types | ||
1039 | // (tuple or object), because the ValueSet type requires homogenous | ||
1040 | // element types. | ||
1041 | // | ||
1042 | // The returned ValueSet can store only values of the receiver's element type. | ||
1043 | func (val Value) AsValueSet() ValueSet { | ||
1044 | if !val.Type().IsCollectionType() { | ||
1045 | panic("not a collection type") | ||
1046 | } | ||
1047 | |||
1048 | // We don't give the caller our own set.Set (assuming we're a cty.Set value) | ||
1049 | // because then the caller could mutate our internals, which is forbidden. | ||
1050 | // Instead, we will construct a new set and append our elements into it. | ||
1051 | ret := NewValueSet(val.Type().ElementType()) | ||
1052 | for it := val.ElementIterator(); it.Next(); { | ||
1053 | _, v := it.Element() | ||
1054 | ret.Add(v) | ||
1055 | } | ||
1056 | return ret | ||
1057 | } | ||
1058 | |||
1059 | // EncapsulatedValue returns the native value encapsulated in a non-null, | ||
1060 | // non-unknown capsule-typed value, or panics if called on any other value. | ||
1061 | // | ||
1062 | // The result is the same pointer that was passed to CapsuleVal to create | ||
1063 | // the value. Since cty considers values to be immutable, it is strongly | ||
1064 | // recommended to treat the encapsulated value itself as immutable too. | ||
1065 | func (val Value) EncapsulatedValue() interface{} { | ||
1066 | if !val.Type().IsCapsuleType() { | ||
1067 | panic("not a capsule-typed value") | ||
1068 | } | ||
1069 | |||
1070 | return val.v | ||
1071 | } | ||