diff options
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/convert/compare_types.go')
-rw-r--r-- | vendor/github.com/zclconf/go-cty/cty/convert/compare_types.go | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/compare_types.go b/vendor/github.com/zclconf/go-cty/cty/convert/compare_types.go new file mode 100644 index 0000000..d84f6ac --- /dev/null +++ b/vendor/github.com/zclconf/go-cty/cty/convert/compare_types.go | |||
@@ -0,0 +1,165 @@ | |||
1 | package convert | ||
2 | |||
3 | import ( | ||
4 | "github.com/zclconf/go-cty/cty" | ||
5 | ) | ||
6 | |||
7 | // compareTypes implements a preference order for unification. | ||
8 | // | ||
9 | // The result of this method is not useful for anything other than unification | ||
10 | // preferences, since it assumes that the caller will verify that any suggested | ||
11 | // conversion is actually possible and it is thus able to to make certain | ||
12 | // optimistic assumptions. | ||
13 | func compareTypes(a cty.Type, b cty.Type) int { | ||
14 | |||
15 | // DynamicPseudoType always has lowest preference, because anything can | ||
16 | // convert to it (it acts as a placeholder for "any type") and we want | ||
17 | // to optimistically assume that any dynamics will converge on matching | ||
18 | // their neighbors. | ||
19 | if a == cty.DynamicPseudoType || b == cty.DynamicPseudoType { | ||
20 | if a != cty.DynamicPseudoType { | ||
21 | return -1 | ||
22 | } | ||
23 | if b != cty.DynamicPseudoType { | ||
24 | return 1 | ||
25 | } | ||
26 | return 0 | ||
27 | } | ||
28 | |||
29 | if a.IsPrimitiveType() && b.IsPrimitiveType() { | ||
30 | // String is a supertype of all primitive types, because we can | ||
31 | // represent all primitive values as specially-formatted strings. | ||
32 | if a == cty.String || b == cty.String { | ||
33 | if a != cty.String { | ||
34 | return 1 | ||
35 | } | ||
36 | if b != cty.String { | ||
37 | return -1 | ||
38 | } | ||
39 | return 0 | ||
40 | } | ||
41 | } | ||
42 | |||
43 | if a.IsListType() && b.IsListType() { | ||
44 | return compareTypes(a.ElementType(), b.ElementType()) | ||
45 | } | ||
46 | if a.IsSetType() && b.IsSetType() { | ||
47 | return compareTypes(a.ElementType(), b.ElementType()) | ||
48 | } | ||
49 | if a.IsMapType() && b.IsMapType() { | ||
50 | return compareTypes(a.ElementType(), b.ElementType()) | ||
51 | } | ||
52 | |||
53 | // From this point on we may have swapped the two items in order to | ||
54 | // simplify our cases. Therefore any non-zero return after this point | ||
55 | // must be multiplied by "swap" to potentially invert the return value | ||
56 | // if needed. | ||
57 | swap := 1 | ||
58 | switch { | ||
59 | case a.IsTupleType() && b.IsListType(): | ||
60 | fallthrough | ||
61 | case a.IsObjectType() && b.IsMapType(): | ||
62 | fallthrough | ||
63 | case a.IsSetType() && b.IsTupleType(): | ||
64 | fallthrough | ||
65 | case a.IsSetType() && b.IsListType(): | ||
66 | a, b = b, a | ||
67 | swap = -1 | ||
68 | } | ||
69 | |||
70 | if b.IsSetType() && (a.IsTupleType() || a.IsListType()) { | ||
71 | // We'll just optimistically assume that the element types are | ||
72 | // unifyable/convertible, and let a second recursive pass | ||
73 | // figure out how to make that so. | ||
74 | return -1 * swap | ||
75 | } | ||
76 | |||
77 | if a.IsListType() && b.IsTupleType() { | ||
78 | // We'll just optimistically assume that the tuple's element types | ||
79 | // can be unified into something compatible with the list's element | ||
80 | // type. | ||
81 | return -1 * swap | ||
82 | } | ||
83 | |||
84 | if a.IsMapType() && b.IsObjectType() { | ||
85 | // We'll just optimistically assume that the object's attribute types | ||
86 | // can be unified into something compatible with the map's element | ||
87 | // type. | ||
88 | return -1 * swap | ||
89 | } | ||
90 | |||
91 | // For object and tuple types, comparing two types doesn't really tell | ||
92 | // the whole story because it may be possible to construct a new type C | ||
93 | // that is the supertype of both A and B by unifying each attribute/element | ||
94 | // separately. That possibility is handled by Unify as a follow-up if | ||
95 | // type sorting is insufficient to produce a valid result. | ||
96 | // | ||
97 | // Here we will take care of the simple possibilities where no new type | ||
98 | // is needed. | ||
99 | if a.IsObjectType() && b.IsObjectType() { | ||
100 | atysA := a.AttributeTypes() | ||
101 | atysB := b.AttributeTypes() | ||
102 | |||
103 | if len(atysA) != len(atysB) { | ||
104 | return 0 | ||
105 | } | ||
106 | |||
107 | hasASuper := false | ||
108 | hasBSuper := false | ||
109 | for k := range atysA { | ||
110 | if _, has := atysB[k]; !has { | ||
111 | return 0 | ||
112 | } | ||
113 | |||
114 | cmp := compareTypes(atysA[k], atysB[k]) | ||
115 | if cmp < 0 { | ||
116 | hasASuper = true | ||
117 | } else if cmp > 0 { | ||
118 | hasBSuper = true | ||
119 | } | ||
120 | } | ||
121 | |||
122 | switch { | ||
123 | case hasASuper && hasBSuper: | ||
124 | return 0 | ||
125 | case hasASuper: | ||
126 | return -1 * swap | ||
127 | case hasBSuper: | ||
128 | return 1 * swap | ||
129 | default: | ||
130 | return 0 | ||
131 | } | ||
132 | } | ||
133 | if a.IsTupleType() && b.IsTupleType() { | ||
134 | etysA := a.TupleElementTypes() | ||
135 | etysB := b.TupleElementTypes() | ||
136 | |||
137 | if len(etysA) != len(etysB) { | ||
138 | return 0 | ||
139 | } | ||
140 | |||
141 | hasASuper := false | ||
142 | hasBSuper := false | ||
143 | for i := range etysA { | ||
144 | cmp := compareTypes(etysA[i], etysB[i]) | ||
145 | if cmp < 0 { | ||
146 | hasASuper = true | ||
147 | } else if cmp > 0 { | ||
148 | hasBSuper = true | ||
149 | } | ||
150 | } | ||
151 | |||
152 | switch { | ||
153 | case hasASuper && hasBSuper: | ||
154 | return 0 | ||
155 | case hasASuper: | ||
156 | return -1 * swap | ||
157 | case hasBSuper: | ||
158 | return 1 * swap | ||
159 | default: | ||
160 | return 0 | ||
161 | } | ||
162 | } | ||
163 | |||
164 | return 0 | ||
165 | } | ||