blob: 187d38751b8cec4021ecc8fdccf4ee427967419a [file] [log] [blame]
package cty
import (
"fmt"
)
type typeObject struct {
typeImplSigil
AttrTypes map[string]Type
}
// Object creates an object type with the given attribute types.
//
// After a map is passed to this function the caller must no longer access it,
// since ownership is transferred to this library.
func Object(attrTypes map[string]Type) Type {
attrTypesNorm := make(map[string]Type, len(attrTypes))
for k, v := range attrTypes {
attrTypesNorm[NormalizeString(k)] = v
}
return Type{
typeObject{
AttrTypes: attrTypesNorm,
},
}
}
func (t typeObject) Equals(other Type) bool {
if ot, ok := other.typeImpl.(typeObject); ok {
if len(t.AttrTypes) != len(ot.AttrTypes) {
// Fast path: if we don't have the same number of attributes
// then we can't possibly be equal. This also avoids the need
// to test attributes in both directions below, since we know
// there can't be extras in "other".
return false
}
for attr, ty := range t.AttrTypes {
oty, ok := ot.AttrTypes[attr]
if !ok {
return false
}
if !oty.Equals(ty) {
return false
}
}
return true
}
return false
}
func (t typeObject) FriendlyName(mode friendlyTypeNameMode) string {
// There isn't really a friendly way to write an object type due to its
// complexity, so we'll just do something English-ish. Callers will
// probably want to make some extra effort to avoid ever printing out
// an object type FriendlyName in its entirety. For example, could
// produce an error message by diffing two object types and saying
// something like "Expected attribute foo to be string, but got number".
// TODO: Finish this
return "object"
}
func (t typeObject) GoString() string {
if len(t.AttrTypes) == 0 {
return "cty.EmptyObject"
}
return fmt.Sprintf("cty.Object(%#v)", t.AttrTypes)
}
// EmptyObject is a shorthand for Object(map[string]Type{}), to more
// easily talk about the empty object type.
var EmptyObject Type
// EmptyObjectVal is the only possible non-null, non-unknown value of type
// EmptyObject.
var EmptyObjectVal Value
func init() {
EmptyObject = Object(map[string]Type{})
EmptyObjectVal = Value{
ty: EmptyObject,
v: map[string]interface{}{},
}
}
// IsObjectType returns true if the given type is an object type, regardless
// of its element type.
func (t Type) IsObjectType() bool {
_, ok := t.typeImpl.(typeObject)
return ok
}
// HasAttribute returns true if the receiver has an attribute with the given
// name, regardless of its type. Will panic if the reciever isn't an object
// type; use IsObjectType to determine whether this operation will succeed.
func (t Type) HasAttribute(name string) bool {
name = NormalizeString(name)
if ot, ok := t.typeImpl.(typeObject); ok {
_, hasAttr := ot.AttrTypes[name]
return hasAttr
}
panic("HasAttribute on non-object Type")
}
// AttributeType returns the type of the attribute with the given name. Will
// panic if the receiver is not an object type (use IsObjectType to confirm)
// or if the object type has no such attribute (use HasAttribute to confirm).
func (t Type) AttributeType(name string) Type {
name = NormalizeString(name)
if ot, ok := t.typeImpl.(typeObject); ok {
aty, hasAttr := ot.AttrTypes[name]
if !hasAttr {
panic("no such attribute")
}
return aty
}
panic("AttributeType on non-object Type")
}
// AttributeTypes returns a map from attribute names to their associated
// types. Will panic if the receiver is not an object type (use IsObjectType
// to confirm).
//
// The returned map is part of the internal state of the type, and is provided
// for read access only. It is forbidden for any caller to modify the returned
// map. For many purposes the attribute-related methods of Value are more
// appropriate and more convenient to use.
func (t Type) AttributeTypes() map[string]Type {
if ot, ok := t.typeImpl.(typeObject); ok {
return ot.AttrTypes
}
panic("AttributeTypes on non-object Type")
}