1package 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. 30type Value struct { 31 ty Type 32 v interface{} 33} 34 35// Type returns the type of the value. 36func (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. 47func (val Value) IsKnown() bool { 48 if val.IsMarked() { 49 return val.unmarkForce().IsKnown() 50 } 51 return val.v != unknown 52} 53 54// IsNull returns true if the value is null. Values of any type can be 55// null, but any operations on a null value will panic. No operation ever 56// produces null, so an application that never introduces Null values can 57// be guaranteed to never receive any either. 58func (val Value) IsNull() bool { 59 if val.IsMarked() { 60 return val.unmarkForce().IsNull() 61 } 62 return val.v == nil 63} 64 65// NilVal is an invalid Value that can be used as a placeholder when returning 66// with an error from a function that returns (Value, error). 67// 68// NilVal is *not* a valid error and so no operations may be performed on it. 69// Any attempt to use it will result in a panic. 70// 71// This should not be confused with the idea of a Null value, as returned by 72// NullVal. NilVal is a nil within the *Go* type system, and is invalid in 73// the cty type system. Null values *do* exist in the cty type system. 74var NilVal = Value{ 75 ty: Type{typeImpl: nil}, 76 v: nil, 77} 78 79// IsWhollyKnown is an extension of IsKnown that also recursively checks 80// inside collections and structures to see if there are any nested unknown 81// values. 82func (val Value) IsWhollyKnown() bool { 83 if val.IsMarked() { 84 return val.unmarkForce().IsWhollyKnown() 85 } 86 87 if !val.IsKnown() { 88 return false 89 } 90 91 if val.IsNull() { 92 // Can't recurse into a null, so we're done 93 return true 94 } 95 96 switch { 97 case val.CanIterateElements(): 98 for it := val.ElementIterator(); it.Next(); { 99 _, ev := it.Element() 100 if !ev.IsWhollyKnown() { 101 return false 102 } 103 } 104 return true 105 default: 106 return true 107 } 108} 109 110// HasWhollyKnownType checks if the value is dynamic, or contains any nested 111// DynamicVal. This implies that both the value is not known, and the final 112// type may change. 113func (val Value) HasWhollyKnownType() bool { 114 // a null dynamic type is known 115 if val.IsNull() { 116 return true 117 } 118 119 // an unknown DynamicPseudoType is a DynamicVal, but we don't want to 120 // check that value for equality here, since this method is used within the 121 // equality check. 122 if !val.IsKnown() && val.ty == DynamicPseudoType { 123 return false 124 } 125 126 if val.CanIterateElements() { 127 // if the value is not known, then we can look directly at the internal 128 // types 129 if !val.IsKnown() { 130 return !val.ty.HasDynamicTypes() 131 } 132 133 for it := val.ElementIterator(); it.Next(); { 134 _, ev := it.Element() 135 if !ev.HasWhollyKnownType() { 136 return false 137 } 138 } 139 } 140 141 return true 142} 143