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