]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
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 | } |