]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package cty |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | ) | |
6 | ||
7 | type typeObject struct { | |
8 | typeImplSigil | |
9 | AttrTypes map[string]Type | |
10 | } | |
11 | ||
12 | // Object creates an object type with the given attribute types. | |
13 | // | |
14 | // After a map is passed to this function the caller must no longer access it, | |
15 | // since ownership is transferred to this library. | |
16 | func Object(attrTypes map[string]Type) Type { | |
17 | attrTypesNorm := make(map[string]Type, len(attrTypes)) | |
18 | for k, v := range attrTypes { | |
19 | attrTypesNorm[NormalizeString(k)] = v | |
20 | } | |
21 | ||
22 | return Type{ | |
23 | typeObject{ | |
24 | AttrTypes: attrTypesNorm, | |
25 | }, | |
26 | } | |
27 | } | |
28 | ||
29 | func (t typeObject) Equals(other Type) bool { | |
30 | if ot, ok := other.typeImpl.(typeObject); ok { | |
31 | if len(t.AttrTypes) != len(ot.AttrTypes) { | |
32 | // Fast path: if we don't have the same number of attributes | |
33 | // then we can't possibly be equal. This also avoids the need | |
34 | // to test attributes in both directions below, since we know | |
35 | // there can't be extras in "other". | |
36 | return false | |
37 | } | |
38 | ||
39 | for attr, ty := range t.AttrTypes { | |
40 | oty, ok := ot.AttrTypes[attr] | |
41 | if !ok { | |
42 | return false | |
43 | } | |
44 | if !oty.Equals(ty) { | |
45 | return false | |
46 | } | |
47 | } | |
48 | ||
49 | return true | |
50 | } | |
51 | return false | |
52 | } | |
53 | ||
107c1cdb | 54 | func (t typeObject) FriendlyName(mode friendlyTypeNameMode) string { |
15c0b25d AP |
55 | // There isn't really a friendly way to write an object type due to its |
56 | // complexity, so we'll just do something English-ish. Callers will | |
57 | // probably want to make some extra effort to avoid ever printing out | |
58 | // an object type FriendlyName in its entirety. For example, could | |
59 | // produce an error message by diffing two object types and saying | |
60 | // something like "Expected attribute foo to be string, but got number". | |
61 | // TODO: Finish this | |
62 | return "object" | |
63 | } | |
64 | ||
65 | func (t typeObject) GoString() string { | |
66 | if len(t.AttrTypes) == 0 { | |
67 | return "cty.EmptyObject" | |
68 | } | |
69 | return fmt.Sprintf("cty.Object(%#v)", t.AttrTypes) | |
70 | } | |
71 | ||
72 | // EmptyObject is a shorthand for Object(map[string]Type{}), to more | |
73 | // easily talk about the empty object type. | |
74 | var EmptyObject Type | |
75 | ||
76 | // EmptyObjectVal is the only possible non-null, non-unknown value of type | |
77 | // EmptyObject. | |
78 | var EmptyObjectVal Value | |
79 | ||
80 | func init() { | |
81 | EmptyObject = Object(map[string]Type{}) | |
82 | EmptyObjectVal = Value{ | |
83 | ty: EmptyObject, | |
84 | v: map[string]interface{}{}, | |
85 | } | |
86 | } | |
87 | ||
88 | // IsObjectType returns true if the given type is an object type, regardless | |
89 | // of its element type. | |
90 | func (t Type) IsObjectType() bool { | |
91 | _, ok := t.typeImpl.(typeObject) | |
92 | return ok | |
93 | } | |
94 | ||
95 | // HasAttribute returns true if the receiver has an attribute with the given | |
96 | // name, regardless of its type. Will panic if the reciever isn't an object | |
97 | // type; use IsObjectType to determine whether this operation will succeed. | |
98 | func (t Type) HasAttribute(name string) bool { | |
99 | name = NormalizeString(name) | |
100 | if ot, ok := t.typeImpl.(typeObject); ok { | |
101 | _, hasAttr := ot.AttrTypes[name] | |
102 | return hasAttr | |
103 | } | |
104 | panic("HasAttribute on non-object Type") | |
105 | } | |
106 | ||
107 | // AttributeType returns the type of the attribute with the given name. Will | |
108 | // panic if the receiver is not an object type (use IsObjectType to confirm) | |
109 | // or if the object type has no such attribute (use HasAttribute to confirm). | |
110 | func (t Type) AttributeType(name string) Type { | |
111 | name = NormalizeString(name) | |
112 | if ot, ok := t.typeImpl.(typeObject); ok { | |
113 | aty, hasAttr := ot.AttrTypes[name] | |
114 | if !hasAttr { | |
115 | panic("no such attribute") | |
116 | } | |
117 | return aty | |
118 | } | |
119 | panic("AttributeType on non-object Type") | |
120 | } | |
121 | ||
122 | // AttributeTypes returns a map from attribute names to their associated | |
123 | // types. Will panic if the receiver is not an object type (use IsObjectType | |
124 | // to confirm). | |
125 | // | |
126 | // The returned map is part of the internal state of the type, and is provided | |
127 | // for read access only. It is forbidden for any caller to modify the returned | |
128 | // map. For many purposes the attribute-related methods of Value are more | |
129 | // appropriate and more convenient to use. | |
130 | func (t Type) AttributeTypes() map[string]Type { | |
131 | if ot, ok := t.typeImpl.(typeObject); ok { | |
132 | return ot.AttrTypes | |
133 | } | |
134 | panic("AttributeTypes on non-object Type") | |
135 | } |