]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package cty |
2 | ||
3 | // Value represents a value of a particular type, and is the interface by | |
4 | // which operations are executed on typed values. | |
5 | // | |
6 | // Value has two different classes of method. Operation methods stay entirely | |
7 | // within the type system (methods accept and return Value instances) and | |
8 | // are intended for use in implementing a language in terms of cty, while | |
9 | // integration methods either enter or leave the type system, working with | |
10 | // native Go values. Operation methods are guaranteed to support all of the | |
11 | // expected short-circuit behavior for unknown and dynamic values, while | |
12 | // integration methods may not. | |
13 | // | |
14 | // The philosophy for the operations API is that it's the caller's | |
15 | // responsibility to ensure that the given types and values satisfy the | |
16 | // specified invariants during a separate type check, so that the caller is | |
17 | // able to return errors to its user from the application's own perspective. | |
18 | // | |
19 | // Consequently the design of these methods assumes such checks have already | |
20 | // been done and panics if any invariants turn out not to be satisfied. These | |
21 | // panic errors are not intended to be handled, but rather indicate a bug in | |
22 | // the calling application that should be fixed with more checks prior to | |
23 | // executing operations. | |
24 | // | |
25 | // A related consequence of this philosophy is that no automatic type | |
26 | // conversions are done. If a method specifies that its argument must be | |
27 | // number then it's the caller's responsibility to do that conversion before | |
28 | // the call, thus allowing the application to have more constrained conversion | |
29 | // rules than are offered by the built-in converter where necessary. | |
30 | type Value struct { | |
31 | ty Type | |
32 | v interface{} | |
33 | } | |
34 | ||
35 | // Type returns the type of the value. | |
36 | func (val Value) Type() Type { | |
37 | return val.ty | |
38 | } | |
39 | ||
40 | // IsKnown returns true if the value is known. That is, if it is not | |
41 | // the result of the unknown value constructor Unknown(...), and is not | |
42 | // the result of an operation on another unknown value. | |
43 | // | |
44 | // Unknown values are only produced either directly or as a result of | |
45 | // operating on other unknown values, and so an application that never | |
46 | // introduces Unknown values can be guaranteed to never receive any either. | |
47 | func (val Value) IsKnown() bool { | |
48 | return val.v != unknown | |
49 | } | |
50 | ||
51 | // IsNull returns true if the value is null. Values of any type can be | |
52 | // null, but any operations on a null value will panic. No operation ever | |
53 | // produces null, so an application that never introduces Null values can | |
54 | // be guaranteed to never receive any either. | |
55 | func (val Value) IsNull() bool { | |
56 | return val.v == nil | |
57 | } | |
58 | ||
59 | // NilVal is an invalid Value that can be used as a placeholder when returning | |
60 | // with an error from a function that returns (Value, error). | |
61 | // | |
62 | // NilVal is *not* a valid error and so no operations may be performed on it. | |
63 | // Any attempt to use it will result in a panic. | |
64 | // | |
65 | // This should not be confused with the idea of a Null value, as returned by | |
66 | // NullVal. NilVal is a nil within the *Go* type system, and is invalid in | |
67 | // the cty type system. Null values *do* exist in the cty type system. | |
68 | var NilVal = Value{ | |
69 | ty: Type{typeImpl: nil}, | |
70 | v: nil, | |
71 | } | |
72 | ||
73 | // IsWhollyKnown is an extension of IsKnown that also recursively checks | |
74 | // inside collections and structures to see if there are any nested unknown | |
75 | // values. | |
76 | func (val Value) IsWhollyKnown() bool { | |
77 | if !val.IsKnown() { | |
78 | return false | |
79 | } | |
80 | ||
81 | if val.IsNull() { | |
82 | // Can't recurse into a null, so we're done | |
83 | return true | |
84 | } | |
85 | ||
86 | switch { | |
87 | case val.CanIterateElements(): | |
88 | for it := val.ElementIterator(); it.Next(); { | |
89 | _, ev := it.Element() | |
90 | if !ev.IsWhollyKnown() { | |
91 | return false | |
92 | } | |
93 | } | |
94 | return true | |
95 | default: | |
96 | return true | |
97 | } | |
98 | } |