package cty // Value represents a value of a particular type, and is the interface by // which operations are executed on typed values. // // Value has two different classes of method. Operation methods stay entirely // within the type system (methods accept and return Value instances) and // are intended for use in implementing a language in terms of cty, while // integration methods either enter or leave the type system, working with // native Go values. Operation methods are guaranteed to support all of the // expected short-circuit behavior for unknown and dynamic values, while // integration methods may not. // // The philosophy for the operations API is that it's the caller's // responsibility to ensure that the given types and values satisfy the // specified invariants during a separate type check, so that the caller is // able to return errors to its user from the application's own perspective. // // Consequently the design of these methods assumes such checks have already // been done and panics if any invariants turn out not to be satisfied. These // panic errors are not intended to be handled, but rather indicate a bug in // the calling application that should be fixed with more checks prior to // executing operations. // // A related consequence of this philosophy is that no automatic type // conversions are done. If a method specifies that its argument must be // number then it's the caller's responsibility to do that conversion before // the call, thus allowing the application to have more constrained conversion // rules than are offered by the built-in converter where necessary. type Value struct { ty Type v interface{} } // Type returns the type of the value. func (val Value) Type() Type { return val.ty } // IsKnown returns true if the value is known. That is, if it is not // the result of the unknown value constructor Unknown(...), and is not // the result of an operation on another unknown value. // // Unknown values are only produced either directly or as a result of // operating on other unknown values, and so an application that never // introduces Unknown values can be guaranteed to never receive any either. func (val Value) IsKnown() bool { return val.v != unknown } // IsNull returns true if the value is null. Values of any type can be // null, but any operations on a null value will panic. No operation ever // produces null, so an application that never introduces Null values can // be guaranteed to never receive any either. func (val Value) IsNull() bool { return val.v == nil } // NilVal is an invalid Value that can be used as a placeholder when returning // with an error from a function that returns (Value, error). // // NilVal is *not* a valid error and so no operations may be performed on it. // Any attempt to use it will result in a panic. // // This should not be confused with the idea of a Null value, as returned by // NullVal. NilVal is a nil within the *Go* type system, and is invalid in // the cty type system. Null values *do* exist in the cty type system. var NilVal = Value{ ty: Type{typeImpl: nil}, v: nil, } // IsWhollyKnown is an extension of IsKnown that also recursively checks // inside collections and structures to see if there are any nested unknown // values. func (val Value) IsWhollyKnown() bool { if !val.IsKnown() { return false } if val.IsNull() { // Can't recurse into a null, so we're done return true } switch { case val.CanIterateElements(): for it := val.ElementIterator(); it.Next(); { _, ev := it.Element() if !ev.IsWhollyKnown() { return false } } return true default: return true } }