terraform-provider-gitea/vendor/github.com/zclconf/go-cty/cty/object_type.go
Malar Invention 00ebcd295e add vendor
2022-04-03 09:37:16 +05:30

221 lines
6.9 KiB
Go

package cty
import (
"fmt"
"sort"
)
type typeObject struct {
typeImplSigil
AttrTypes map[string]Type
AttrOptional map[string]struct{}
}
// 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 {
return ObjectWithOptionalAttrs(attrTypes, nil)
}
// ObjectWithOptionalAttrs creates an object type where some of its attributes
// are optional.
//
// This function is EXPERIMENTAL. The behavior of the function or of any other
// functions working either directly or indirectly with a type created by
// this function is not currently considered as a compatibility constraint, and
// is subject to change even in minor-version releases of this module. Other
// modules that work with cty types and values may or may not support object
// types with optional attributes; if they do not, their behavior when
// receiving one may be non-ideal.
//
// Optional attributes are significant only when an object type is being used
// as a target type for conversion in the "convert" package. A value of an
// object type always has a value for each of the attributes in the attribute
// types table, with optional values replaced with null during conversion.
//
// All keys in the optional slice must also exist in the attrTypes map. If not,
// this function will panic.
//
// After a map or array is passed to this function the caller must no longer
// access it, since ownership is transferred to this library.
func ObjectWithOptionalAttrs(attrTypes map[string]Type, optional []string) Type {
attrTypesNorm := make(map[string]Type, len(attrTypes))
for k, v := range attrTypes {
attrTypesNorm[NormalizeString(k)] = v
}
var optionalSet map[string]struct{}
if len(optional) > 0 {
optionalSet = make(map[string]struct{}, len(optional))
for _, k := range optional {
k = NormalizeString(k)
if _, exists := attrTypesNorm[k]; !exists {
panic(fmt.Sprintf("optional contains undeclared attribute %q", k))
}
optionalSet[k] = struct{}{}
}
}
return Type{
typeObject{
AttrTypes: attrTypesNorm,
AttrOptional: optionalSet,
},
}
}
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
}
_, opt := t.AttrOptional[attr]
_, oopt := ot.AttrOptional[attr]
if opt != oopt {
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"
}
if len(t.AttrOptional) > 0 {
var opt []string
for k := range t.AttrOptional {
opt = append(opt, k)
}
sort.Strings(opt)
return fmt.Sprintf("cty.ObjectWithOptionalAttrs(%#v, %#v)", t.AttrTypes, opt)
}
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")
}
// OptionalAttributes returns a map representing the set of attributes
// that are optional. 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.
func (t Type) OptionalAttributes() map[string]struct{} {
if ot, ok := t.typeImpl.(typeObject); ok {
return ot.AttrOptional
}
panic("OptionalAttributes on non-object Type")
}
// AttributeOptional returns true if the attribute of the given name is
// optional.
//
// 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) AttributeOptional(name string) bool {
name = NormalizeString(name)
if ot, ok := t.typeImpl.(typeObject); ok {
if _, hasAttr := ot.AttrTypes[name]; !hasAttr {
panic("no such attribute")
}
_, exists := ot.AttrOptional[name]
return exists
}
panic("AttributeDefaultValue on non-object Type")
}