aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/zclconf/go-cty/cty/convert
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/convert')
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/compare_types.go165
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/conversion.go120
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go226
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/conversion_dynamic.go33
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go50
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/doc.go15
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/public.go83
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/sort_types.go69
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/unify.go66
9 files changed, 827 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 @@
1package convert
2
3import (
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.
13func 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}
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/conversion.go b/vendor/github.com/zclconf/go-cty/cty/convert/conversion.go
new file mode 100644
index 0000000..7bfcc08
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion.go
@@ -0,0 +1,120 @@
1package convert
2
3import (
4 "github.com/zclconf/go-cty/cty"
5)
6
7// conversion is an internal variant of Conversion that carries around
8// a cty.Path to be used in error responses.
9type conversion func(cty.Value, cty.Path) (cty.Value, error)
10
11func getConversion(in cty.Type, out cty.Type, unsafe bool) conversion {
12 conv := getConversionKnown(in, out, unsafe)
13 if conv == nil {
14 return nil
15 }
16
17 // Wrap the conversion in some standard checks that we don't want to
18 // have to repeat in every conversion function.
19 return func(in cty.Value, path cty.Path) (cty.Value, error) {
20 if !in.IsKnown() {
21 return cty.UnknownVal(out), nil
22 }
23 if in.IsNull() {
24 // We'll pass through nulls, albeit type converted, and let
25 // the caller deal with whatever handling they want to do in
26 // case null values are considered valid in some applications.
27 return cty.NullVal(out), nil
28 }
29
30 return conv(in, path)
31 }
32}
33
34func getConversionKnown(in cty.Type, out cty.Type, unsafe bool) conversion {
35 switch {
36
37 case out == cty.DynamicPseudoType:
38 // Conversion *to* DynamicPseudoType means that the caller wishes
39 // to allow any type in this position, so we'll produce a do-nothing
40 // conversion that just passes through the value as-is.
41 return dynamicPassthrough
42
43 case unsafe && in == cty.DynamicPseudoType:
44 // Conversion *from* DynamicPseudoType means that we have a value
45 // whose type isn't yet known during type checking. For these we will
46 // assume that conversion will succeed and deal with any errors that
47 // result (which is why we can only do this when "unsafe" is set).
48 return dynamicFixup(out)
49
50 case in.IsPrimitiveType() && out.IsPrimitiveType():
51 conv := primitiveConversionsSafe[in][out]
52 if conv != nil {
53 return conv
54 }
55 if unsafe {
56 return primitiveConversionsUnsafe[in][out]
57 }
58 return nil
59
60 case out.IsListType() && (in.IsListType() || in.IsSetType()):
61 inEty := in.ElementType()
62 outEty := out.ElementType()
63 if inEty.Equals(outEty) {
64 // This indicates that we're converting from list to set with
65 // the same element type, so we don't need an element converter.
66 return conversionCollectionToList(outEty, nil)
67 }
68
69 convEty := getConversion(inEty, outEty, unsafe)
70 if convEty == nil {
71 return nil
72 }
73 return conversionCollectionToList(outEty, convEty)
74
75 case out.IsSetType() && (in.IsListType() || in.IsSetType()):
76 if in.IsListType() && !unsafe {
77 // Conversion from list to map is unsafe because it will lose
78 // information: the ordering will not be preserved, and any
79 // duplicate elements will be conflated.
80 return nil
81 }
82 inEty := in.ElementType()
83 outEty := out.ElementType()
84 convEty := getConversion(inEty, outEty, unsafe)
85 if inEty.Equals(outEty) {
86 // This indicates that we're converting from set to list with
87 // the same element type, so we don't need an element converter.
88 return conversionCollectionToSet(outEty, nil)
89 }
90
91 if convEty == nil {
92 return nil
93 }
94 return conversionCollectionToSet(outEty, convEty)
95
96 case out.IsListType() && in.IsTupleType():
97 outEty := out.ElementType()
98 return conversionTupleToList(in, outEty, unsafe)
99
100 case out.IsMapType() && in.IsObjectType():
101 outEty := out.ElementType()
102 return conversionObjectToMap(in, outEty, unsafe)
103
104 default:
105 return nil
106
107 }
108}
109
110// retConversion wraps a conversion (internal type) so it can be returned
111// as a Conversion (public type).
112func retConversion(conv conversion) Conversion {
113 if conv == nil {
114 return nil
115 }
116
117 return func(in cty.Value) (cty.Value, error) {
118 return conv(in, cty.Path(nil))
119 }
120}
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go
new file mode 100644
index 0000000..eace85d
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go
@@ -0,0 +1,226 @@
1package convert
2
3import (
4 "github.com/zclconf/go-cty/cty"
5)
6
7// conversionCollectionToList returns a conversion that will apply the given
8// conversion to all of the elements of a collection (something that supports
9// ForEachElement and LengthInt) and then returns the result as a list.
10//
11// "conv" can be nil if the elements are expected to already be of the
12// correct type and just need to be re-wrapped into a list. (For example,
13// if we're converting from a set into a list of the same element type.)
14func conversionCollectionToList(ety cty.Type, conv conversion) conversion {
15 return func(val cty.Value, path cty.Path) (cty.Value, error) {
16 elems := make([]cty.Value, 0, val.LengthInt())
17 i := int64(0)
18 path = append(path, nil)
19 it := val.ElementIterator()
20 for it.Next() {
21 _, val := it.Element()
22 var err error
23
24 path[len(path)-1] = cty.IndexStep{
25 Key: cty.NumberIntVal(i),
26 }
27
28 if conv != nil {
29 val, err = conv(val, path)
30 if err != nil {
31 return cty.NilVal, err
32 }
33 }
34 elems = append(elems, val)
35
36 i++
37 }
38
39 if len(elems) == 0 {
40 return cty.ListValEmpty(ety), nil
41 }
42
43 return cty.ListVal(elems), nil
44 }
45}
46
47// conversionCollectionToSet returns a conversion that will apply the given
48// conversion to all of the elements of a collection (something that supports
49// ForEachElement and LengthInt) and then returns the result as a set.
50//
51// "conv" can be nil if the elements are expected to already be of the
52// correct type and just need to be re-wrapped into a set. (For example,
53// if we're converting from a list into a set of the same element type.)
54func conversionCollectionToSet(ety cty.Type, conv conversion) conversion {
55 return func(val cty.Value, path cty.Path) (cty.Value, error) {
56 elems := make([]cty.Value, 0, val.LengthInt())
57 i := int64(0)
58 path = append(path, nil)
59 it := val.ElementIterator()
60 for it.Next() {
61 _, val := it.Element()
62 var err error
63
64 path[len(path)-1] = cty.IndexStep{
65 Key: cty.NumberIntVal(i),
66 }
67
68 if conv != nil {
69 val, err = conv(val, path)
70 if err != nil {
71 return cty.NilVal, err
72 }
73 }
74 elems = append(elems, val)
75
76 i++
77 }
78
79 if len(elems) == 0 {
80 return cty.SetValEmpty(ety), nil
81 }
82
83 return cty.SetVal(elems), nil
84 }
85}
86
87// conversionTupleToList returns a conversion that will take a value of the
88// given tuple type and return a list of the given element type.
89//
90// Will panic if the given tupleType isn't actually a tuple type.
91func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) conversion {
92 tupleEtys := tupleType.TupleElementTypes()
93
94 if len(tupleEtys) == 0 {
95 // Empty tuple short-circuit
96 return func(val cty.Value, path cty.Path) (cty.Value, error) {
97 return cty.ListValEmpty(listEty), nil
98 }
99 }
100
101 if listEty == cty.DynamicPseudoType {
102 // This is a special case where the caller wants us to find
103 // a suitable single type that all elements can convert to, if
104 // possible.
105 listEty, _ = unify(tupleEtys, unsafe)
106 if listEty == cty.NilType {
107 return nil
108 }
109 }
110
111 elemConvs := make([]conversion, len(tupleEtys))
112 for i, tupleEty := range tupleEtys {
113 if tupleEty.Equals(listEty) {
114 // no conversion required
115 continue
116 }
117
118 elemConvs[i] = getConversion(tupleEty, listEty, unsafe)
119 if elemConvs[i] == nil {
120 // If any of our element conversions are impossible, then the our
121 // whole conversion is impossible.
122 return nil
123 }
124 }
125
126 // If we fall out here then a conversion is possible, using the
127 // element conversions in elemConvs
128 return func(val cty.Value, path cty.Path) (cty.Value, error) {
129 elems := make([]cty.Value, 0, len(elemConvs))
130 path = append(path, nil)
131 i := int64(0)
132 it := val.ElementIterator()
133 for it.Next() {
134 _, val := it.Element()
135 var err error
136
137 path[len(path)-1] = cty.IndexStep{
138 Key: cty.NumberIntVal(i),
139 }
140
141 conv := elemConvs[i]
142 if conv != nil {
143 val, err = conv(val, path)
144 if err != nil {
145 return cty.NilVal, err
146 }
147 }
148 elems = append(elems, val)
149
150 i++
151 }
152
153 return cty.ListVal(elems), nil
154 }
155}
156
157// conversionObjectToMap returns a conversion that will take a value of the
158// given object type and return a map of the given element type.
159//
160// Will panic if the given objectType isn't actually an object type.
161func conversionObjectToMap(objectType cty.Type, mapEty cty.Type, unsafe bool) conversion {
162 objectAtys := objectType.AttributeTypes()
163
164 if len(objectAtys) == 0 {
165 // Empty object short-circuit
166 return func(val cty.Value, path cty.Path) (cty.Value, error) {
167 return cty.MapValEmpty(mapEty), nil
168 }
169 }
170
171 if mapEty == cty.DynamicPseudoType {
172 // This is a special case where the caller wants us to find
173 // a suitable single type that all elements can convert to, if
174 // possible.
175 objectAtysList := make([]cty.Type, 0, len(objectAtys))
176 for _, aty := range objectAtys {
177 objectAtysList = append(objectAtysList, aty)
178 }
179 mapEty, _ = unify(objectAtysList, unsafe)
180 if mapEty == cty.NilType {
181 return nil
182 }
183 }
184
185 elemConvs := make(map[string]conversion, len(objectAtys))
186 for name, objectAty := range objectAtys {
187 if objectAty.Equals(mapEty) {
188 // no conversion required
189 continue
190 }
191
192 elemConvs[name] = getConversion(objectAty, mapEty, unsafe)
193 if elemConvs[name] == nil {
194 // If any of our element conversions are impossible, then the our
195 // whole conversion is impossible.
196 return nil
197 }
198 }
199
200 // If we fall out here then a conversion is possible, using the
201 // element conversions in elemConvs
202 return func(val cty.Value, path cty.Path) (cty.Value, error) {
203 elems := make(map[string]cty.Value, len(elemConvs))
204 path = append(path, nil)
205 it := val.ElementIterator()
206 for it.Next() {
207 name, val := it.Element()
208 var err error
209
210 path[len(path)-1] = cty.IndexStep{
211 Key: name,
212 }
213
214 conv := elemConvs[name.AsString()]
215 if conv != nil {
216 val, err = conv(val, path)
217 if err != nil {
218 return cty.NilVal, err
219 }
220 }
221 elems[name.AsString()] = val
222 }
223
224 return cty.MapVal(elems), nil
225 }
226}
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_dynamic.go b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_dynamic.go
new file mode 100644
index 0000000..4d19cf6
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_dynamic.go
@@ -0,0 +1,33 @@
1package convert
2
3import (
4 "github.com/zclconf/go-cty/cty"
5)
6
7// dynamicFixup deals with just-in-time conversions of values that were
8// input-typed as cty.DynamicPseudoType during analysis, ensuring that
9// we end up with the desired output type once the value is known, or
10// failing with an error if that is not possible.
11//
12// This is in the spirit of the cty philosophy of optimistically assuming that
13// DynamicPseudoType values will become the intended value eventually, and
14// dealing with any inconsistencies during final evaluation.
15func dynamicFixup(wantType cty.Type) conversion {
16 return func(in cty.Value, path cty.Path) (cty.Value, error) {
17 ret, err := Convert(in, wantType)
18 if err != nil {
19 // Re-wrap this error so that the returned path is relative
20 // to the caller's original value, rather than relative to our
21 // conversion value here.
22 return cty.NilVal, path.NewError(err)
23 }
24 return ret, nil
25 }
26}
27
28// dynamicPassthrough is an identity conversion that is used when the
29// target type is DynamicPseudoType, indicating that the caller doesn't care
30// which type is returned.
31func dynamicPassthrough(in cty.Value, path cty.Path) (cty.Value, error) {
32 return in, nil
33}
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go
new file mode 100644
index 0000000..e563ee3
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go
@@ -0,0 +1,50 @@
1package convert
2
3import (
4 "math/big"
5
6 "github.com/zclconf/go-cty/cty"
7)
8
9var stringTrue = cty.StringVal("true")
10var stringFalse = cty.StringVal("false")
11
12var primitiveConversionsSafe = map[cty.Type]map[cty.Type]conversion{
13 cty.Number: {
14 cty.String: func(val cty.Value, path cty.Path) (cty.Value, error) {
15 f := val.AsBigFloat()
16 return cty.StringVal(f.Text('f', -1)), nil
17 },
18 },
19 cty.Bool: {
20 cty.String: func(val cty.Value, path cty.Path) (cty.Value, error) {
21 if val.True() {
22 return stringTrue, nil
23 } else {
24 return stringFalse, nil
25 }
26 },
27 },
28}
29
30var primitiveConversionsUnsafe = map[cty.Type]map[cty.Type]conversion{
31 cty.String: {
32 cty.Number: func(val cty.Value, path cty.Path) (cty.Value, error) {
33 f, _, err := big.ParseFloat(val.AsString(), 10, 512, big.ToNearestEven)
34 if err != nil {
35 return cty.NilVal, path.NewErrorf("a number is required")
36 }
37 return cty.NumberVal(f), nil
38 },
39 cty.Bool: func(val cty.Value, path cty.Path) (cty.Value, error) {
40 switch val.AsString() {
41 case "true", "1":
42 return cty.True, nil
43 case "false", "0":
44 return cty.False, nil
45 default:
46 return cty.NilVal, path.NewErrorf("a bool is required")
47 }
48 },
49 },
50}
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/doc.go b/vendor/github.com/zclconf/go-cty/cty/convert/doc.go
new file mode 100644
index 0000000..2037299
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/doc.go
@@ -0,0 +1,15 @@
1// Package convert contains some routines for converting between cty types.
2// The intent of providing this package is to encourage applications using
3// cty to have consistent type conversion behavior for maximal interoperability
4// when Values pass from one application to another.
5//
6// The conversions are categorized into two categories. "Safe" conversions are
7// ones that are guaranteed to succeed if given a non-null value of the
8// appropriate source type. "Unsafe" conversions, on the other hand, are valid
9// for only a subset of input values, and thus may fail with an error when
10// called for values outside of that valid subset.
11//
12// The functions whose names end in Unsafe support all of the conversions that
13// are supported by the corresponding functions whose names do not have that
14// suffix, and then additional unsafe conversions as well.
15package convert
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/public.go b/vendor/github.com/zclconf/go-cty/cty/convert/public.go
new file mode 100644
index 0000000..55f44ae
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/public.go
@@ -0,0 +1,83 @@
1package convert
2
3import (
4 "fmt"
5
6 "github.com/zclconf/go-cty/cty"
7)
8
9// This file contains the public interface of this package, which is intended
10// to be a small, convenient interface designed for easy integration into
11// a hypothetical language type checker and interpreter.
12
13// Conversion is a named function type representing a conversion from a
14// value of one type to a value of another type.
15//
16// The source type for a conversion is always the source type given to
17// the function that returned the Conversion, but there is no way to recover
18// that from a Conversion value itself. If a Conversion is given a value
19// that is not of its expected type (with the exception of DynamicPseudoType,
20// which is always supported) then the function may panic or produce undefined
21// results.
22type Conversion func(in cty.Value) (out cty.Value, err error)
23
24// GetConversion returns a Conversion between the given in and out Types if
25// a safe one is available, or returns nil otherwise.
26func GetConversion(in cty.Type, out cty.Type) Conversion {
27 return retConversion(getConversion(in, out, false))
28}
29
30// GetConversionUnsafe returns a Conversion between the given in and out Types
31// if either a safe or unsafe one is available, or returns nil otherwise.
32func GetConversionUnsafe(in cty.Type, out cty.Type) Conversion {
33 return retConversion(getConversion(in, out, true))
34}
35
36// Convert returns the result of converting the given value to the given type
37// if an safe or unsafe conversion is available, or returns an error if such a
38// conversion is impossible.
39//
40// This is a convenience wrapper around calling GetConversionUnsafe and then
41// immediately passing the given value to the resulting function.
42func Convert(in cty.Value, want cty.Type) (cty.Value, error) {
43 if in.Type().Equals(want) {
44 return in, nil
45 }
46
47 conv := GetConversionUnsafe(in.Type(), want)
48 if conv == nil {
49 return cty.NilVal, fmt.Errorf("incorrect type; %s required", want.FriendlyName())
50 }
51 return conv(in)
52}
53
54// Unify attempts to find the most general type that can be converted from
55// all of the given types. If this is possible, that type is returned along
56// with a slice of necessary conversions for some of the given types.
57//
58// If no common supertype can be found, this function returns cty.NilType and
59// a nil slice.
60//
61// If a common supertype *can* be found, the returned slice will always be
62// non-nil and will contain a non-nil conversion for each given type that
63// needs to be converted, with indices corresponding to the input slice.
64// Any given type that does *not* need conversion (because it is already of
65// the appropriate type) will have a nil Conversion.
66//
67// cty.DynamicPseudoType is, as usual, a special case. If the given type list
68// contains a mixture of dynamic and non-dynamic types, the dynamic types are
69// disregarded for type selection and a conversion is returned for them that
70// will attempt a late conversion of the given value to the target type,
71// failing with a conversion error if the eventual concrete type is not
72// compatible. If *all* given types are DynamicPseudoType, or in the
73// degenerate case of an empty slice of types, the returned type is itself
74// cty.DynamicPseudoType and no conversions are attempted.
75func Unify(types []cty.Type) (cty.Type, []Conversion) {
76 return unify(types, false)
77}
78
79// UnifyUnsafe is the same as Unify except that it may return unsafe
80// conversions in situations where a safe conversion isn't also available.
81func UnifyUnsafe(types []cty.Type) (cty.Type, []Conversion) {
82 return unify(types, true)
83}
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/sort_types.go b/vendor/github.com/zclconf/go-cty/cty/convert/sort_types.go
new file mode 100644
index 0000000..b776910
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/sort_types.go
@@ -0,0 +1,69 @@
1package convert
2
3import (
4 "github.com/zclconf/go-cty/cty"
5)
6
7// sortTypes produces an ordering of the given types that serves as a
8// preference order for the result of unification of the given types.
9// The return value is a slice of indices into the given slice, and will
10// thus always be the same length as the given slice.
11//
12// The goal is that the most general of the given types will appear first
13// in the ordering. If there are uncomparable pairs of types in the list
14// then they will appear in an undefined order, and the unification pass
15// will presumably then fail.
16func sortTypes(tys []cty.Type) []int {
17 l := len(tys)
18
19 // First we build a graph whose edges represent "more general than",
20 // which we will then do a topological sort of.
21 edges := make([][]int, l)
22 for i := 0; i < (l - 1); i++ {
23 for j := i + 1; j < l; j++ {
24 cmp := compareTypes(tys[i], tys[j])
25 switch {
26 case cmp < 0:
27 edges[i] = append(edges[i], j)
28 case cmp > 0:
29 edges[j] = append(edges[j], i)
30 }
31 }
32 }
33
34 // Compute the in-degree of each node
35 inDegree := make([]int, l)
36 for _, outs := range edges {
37 for _, j := range outs {
38 inDegree[j]++
39 }
40 }
41
42 // The array backing our result will double as our queue for visiting
43 // the nodes, with the queue slice moving along this array until it
44 // is empty and positioned at the end of the array. Thus our visiting
45 // order is also our result order.
46 result := make([]int, l)
47 queue := result[0:0]
48
49 // Initialize the queue with any item of in-degree 0, preserving
50 // their relative order.
51 for i, n := range inDegree {
52 if n == 0 {
53 queue = append(queue, i)
54 }
55 }
56
57 for len(queue) != 0 {
58 i := queue[0]
59 queue = queue[1:]
60 for _, j := range edges[i] {
61 inDegree[j]--
62 if inDegree[j] == 0 {
63 queue = append(queue, j)
64 }
65 }
66 }
67
68 return result
69}
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/unify.go b/vendor/github.com/zclconf/go-cty/cty/convert/unify.go
new file mode 100644
index 0000000..bd6736b
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/unify.go
@@ -0,0 +1,66 @@
1package convert
2
3import (
4 "github.com/zclconf/go-cty/cty"
5)
6
7// The current unify implementation is somewhat inefficient, but we accept this
8// under the assumption that it will generally be used with small numbers of
9// types and with types of reasonable complexity. However, it does have a
10// "happy path" where all of the given types are equal.
11//
12// This function is likely to have poor performance in cases where any given
13// types are very complex (lots of deeply-nested structures) or if the list
14// of types itself is very large. In particular, it will walk the nested type
15// structure under the given types several times, especially when given a
16// list of types for which unification is not possible, since each permutation
17// will be tried to determine that result.
18func unify(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
19 if len(types) == 0 {
20 // Degenerate case
21 return cty.NilType, nil
22 }
23
24 prefOrder := sortTypes(types)
25
26 // sortTypes gives us an order where earlier items are preferable as
27 // our result type. We'll now walk through these and choose the first
28 // one we encounter for which conversions exist for all source types.
29 conversions := make([]Conversion, len(types))
30Preferences:
31 for _, wantTypeIdx := range prefOrder {
32 wantType := types[wantTypeIdx]
33 for i, tryType := range types {
34 if i == wantTypeIdx {
35 // Don't need to convert our wanted type to itself
36 conversions[i] = nil
37 continue
38 }
39
40 if tryType.Equals(wantType) {
41 conversions[i] = nil
42 continue
43 }
44
45 if unsafe {
46 conversions[i] = GetConversionUnsafe(tryType, wantType)
47 } else {
48 conversions[i] = GetConversion(tryType, wantType)
49 }
50
51 if conversions[i] == nil {
52 // wantType is not a suitable unification type, so we'll
53 // try the next one in our preference order.
54 continue Preferences
55 }
56 }
57
58 return wantType, conversions
59 }
60
61 // TODO: For structural types, try to invent a new type that they
62 // can all be unified to, by unifying their respective attributes.
63
64 // If we fall out here, no unification is possible
65 return cty.NilType, nil
66}