1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
package cty
// Type represents value types within the type system.
//
// This is a closed interface type, meaning that only the concrete
// implementations provided within this package are considered valid.
type Type struct {
typeImpl
}
type typeImpl interface {
// isTypeImpl is a do-nothing method that exists only to express
// that a type is an implementation of typeImpl.
isTypeImpl() typeImplSigil
// Equals returns true if the other given Type exactly equals the
// receiver Type.
Equals(other Type) bool
// FriendlyName returns a human-friendly *English* name for the given
// type.
FriendlyName(mode friendlyTypeNameMode) string
// GoString implements the GoStringer interface from package fmt.
GoString() string
}
// Base implementation of Type to embed into concrete implementations
// to signal that they are implementations of Type.
type typeImplSigil struct{}
func (t typeImplSigil) isTypeImpl() typeImplSigil {
return typeImplSigil{}
}
// Equals returns true if the other given Type exactly equals the receiver
// type.
func (t Type) Equals(other Type) bool {
if t == NilType || other == NilType {
return t == other
}
return t.typeImpl.Equals(other)
}
// FriendlyName returns a human-friendly *English* name for the given type.
func (t Type) FriendlyName() string {
return t.typeImpl.FriendlyName(friendlyTypeName)
}
// FriendlyNameForConstraint is similar to FriendlyName except that the
// result is specialized for describing type _constraints_ rather than types
// themselves. This is more appropriate when reporting that a particular value
// does not conform to an expected type constraint.
//
// In particular, this function uses the term "any type" to refer to
// cty.DynamicPseudoType, rather than "dynamic" as returned by FriendlyName.
func (t Type) FriendlyNameForConstraint() string {
return t.typeImpl.FriendlyName(friendlyTypeConstraintName)
}
// friendlyNameMode is an internal combination of the various FriendlyName*
// variants that just directly takes a mode, for easy passthrough for
// recursive name construction.
func (t Type) friendlyNameMode(mode friendlyTypeNameMode) string {
return t.typeImpl.FriendlyName(mode)
}
// GoString returns a string approximating how the receiver type would be
// expressed in Go source code.
func (t Type) GoString() string {
if t.typeImpl == nil {
return "cty.NilType"
}
return t.typeImpl.GoString()
}
// NilType is an invalid type used when a function is returning an error
// and has no useful type to return. It should not be used and any methods
// called on it will panic.
var NilType = Type{}
// HasDynamicTypes returns true either if the receiver is itself
// DynamicPseudoType or if it is a compound type whose descendent elements
// are DynamicPseudoType.
func (t Type) HasDynamicTypes() bool {
switch {
case t == DynamicPseudoType:
return true
case t.IsPrimitiveType():
return false
case t.IsCollectionType():
return t.ElementType().HasDynamicTypes()
case t.IsObjectType():
attrTypes := t.AttributeTypes()
for _, at := range attrTypes {
if at.HasDynamicTypes() {
return true
}
}
return false
case t.IsTupleType():
elemTypes := t.TupleElementTypes()
for _, et := range elemTypes {
if et.HasDynamicTypes() {
return true
}
}
return false
case t.IsCapsuleType():
return false
default:
// Should never happen, since above should be exhaustive
panic("HasDynamicTypes does not support the given type")
}
}
// WithoutOptionalAttributesDeep returns a type equivalent to the receiver but
// with any objects with optional attributes converted into fully concrete
// object types. This operation is applied recursively.
func (t Type) WithoutOptionalAttributesDeep() Type {
switch {
case t == DynamicPseudoType, t.IsPrimitiveType(), t.IsCapsuleType():
return t
case t.IsMapType():
return Map(t.ElementType().WithoutOptionalAttributesDeep())
case t.IsListType():
return List(t.ElementType().WithoutOptionalAttributesDeep())
case t.IsSetType():
return Set(t.ElementType().WithoutOptionalAttributesDeep())
case t.IsTupleType():
originalElemTypes := t.TupleElementTypes()
elemTypes := make([]Type, len(originalElemTypes))
for i, et := range originalElemTypes {
elemTypes[i] = et.WithoutOptionalAttributesDeep()
}
return Tuple(elemTypes)
case t.IsObjectType():
originalAttrTypes := t.AttributeTypes()
attrTypes := make(map[string]Type, len(originalAttrTypes))
for k, t := range originalAttrTypes {
attrTypes[k] = t.WithoutOptionalAttributesDeep()
}
// This is the subtle line which does all the work of this function: by
// constructing a new Object type with these attribute types, we drop
// the list of optional attributes (if present). This results in a
// concrete Object type which requires all of the original attributes.
return Object(attrTypes)
default:
// Should never happen, since above should be exhaustive
panic("WithoutOptionalAttributesDeep does not support the given type")
}
}
type friendlyTypeNameMode rune
const (
friendlyTypeName friendlyTypeNameMode = 'N'
friendlyTypeConstraintName friendlyTypeNameMode = 'C'
)
|