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/conversion.go23
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go114
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/conversion_object.go76
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go6
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/conversion_tuple.go71
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/mismatch_msg.go220
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/public.go4
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/convert/unify.go254
8 files changed, 759 insertions, 9 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/conversion.go b/vendor/github.com/zclconf/go-cty/cty/convert/conversion.go
index 7bfcc08..f9aacb4 100644
--- a/vendor/github.com/zclconf/go-cty/cty/convert/conversion.go
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion.go
@@ -17,6 +17,10 @@ func getConversion(in cty.Type, out cty.Type, unsafe bool) conversion {
17 // Wrap the conversion in some standard checks that we don't want to 17 // Wrap the conversion in some standard checks that we don't want to
18 // have to repeat in every conversion function. 18 // have to repeat in every conversion function.
19 return func(in cty.Value, path cty.Path) (cty.Value, error) { 19 return func(in cty.Value, path cty.Path) (cty.Value, error) {
20 if out == cty.DynamicPseudoType {
21 // Conversion to DynamicPseudoType always just passes through verbatim.
22 return in, nil
23 }
20 if !in.IsKnown() { 24 if !in.IsKnown() {
21 return cty.UnknownVal(out), nil 25 return cty.UnknownVal(out), nil
22 } 26 }
@@ -57,6 +61,12 @@ func getConversionKnown(in cty.Type, out cty.Type, unsafe bool) conversion {
57 } 61 }
58 return nil 62 return nil
59 63
64 case out.IsObjectType() && in.IsObjectType():
65 return conversionObjectToObject(in, out, unsafe)
66
67 case out.IsTupleType() && in.IsTupleType():
68 return conversionTupleToTuple(in, out, unsafe)
69
60 case out.IsListType() && (in.IsListType() || in.IsSetType()): 70 case out.IsListType() && (in.IsListType() || in.IsSetType()):
61 inEty := in.ElementType() 71 inEty := in.ElementType()
62 outEty := out.ElementType() 72 outEty := out.ElementType()
@@ -93,10 +103,23 @@ func getConversionKnown(in cty.Type, out cty.Type, unsafe bool) conversion {
93 } 103 }
94 return conversionCollectionToSet(outEty, convEty) 104 return conversionCollectionToSet(outEty, convEty)
95 105
106 case out.IsMapType() && in.IsMapType():
107 inEty := in.ElementType()
108 outEty := out.ElementType()
109 convEty := getConversion(inEty, outEty, unsafe)
110 if convEty == nil {
111 return nil
112 }
113 return conversionCollectionToMap(outEty, convEty)
114
96 case out.IsListType() && in.IsTupleType(): 115 case out.IsListType() && in.IsTupleType():
97 outEty := out.ElementType() 116 outEty := out.ElementType()
98 return conversionTupleToList(in, outEty, unsafe) 117 return conversionTupleToList(in, outEty, unsafe)
99 118
119 case out.IsSetType() && in.IsTupleType():
120 outEty := out.ElementType()
121 return conversionTupleToSet(in, outEty, unsafe)
122
100 case out.IsMapType() && in.IsObjectType(): 123 case out.IsMapType() && in.IsObjectType():
101 outEty := out.ElementType() 124 outEty := out.ElementType()
102 return conversionObjectToMap(in, outEty, unsafe) 125 return conversionObjectToMap(in, outEty, unsafe)
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
index eace85d..3039ba2 100644
--- a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go
@@ -84,6 +84,120 @@ func conversionCollectionToSet(ety cty.Type, conv conversion) conversion {
84 } 84 }
85} 85}
86 86
87// conversionCollectionToMap returns a conversion that will apply the given
88// conversion to all of the elements of a collection (something that supports
89// ForEachElement and LengthInt) and then returns the result as a map.
90//
91// "conv" can be nil if the elements are expected to already be of the
92// correct type and just need to be re-wrapped into a map.
93func conversionCollectionToMap(ety cty.Type, conv conversion) conversion {
94 return func(val cty.Value, path cty.Path) (cty.Value, error) {
95 elems := make(map[string]cty.Value, 0)
96 path = append(path, nil)
97 it := val.ElementIterator()
98 for it.Next() {
99 key, val := it.Element()
100 var err error
101
102 path[len(path)-1] = cty.IndexStep{
103 Key: key,
104 }
105
106 keyStr, err := Convert(key, cty.String)
107 if err != nil {
108 // Should never happen, because keys can only be numbers or
109 // strings and both can convert to string.
110 return cty.DynamicVal, path.NewErrorf("cannot convert key type %s to string for map", key.Type().FriendlyName())
111 }
112
113 if conv != nil {
114 val, err = conv(val, path)
115 if err != nil {
116 return cty.NilVal, err
117 }
118 }
119
120 elems[keyStr.AsString()] = val
121 }
122
123 if len(elems) == 0 {
124 return cty.MapValEmpty(ety), nil
125 }
126
127 return cty.MapVal(elems), nil
128 }
129}
130
131// conversionTupleToSet returns a conversion that will take a value of the
132// given tuple type and return a set of the given element type.
133//
134// Will panic if the given tupleType isn't actually a tuple type.
135func conversionTupleToSet(tupleType cty.Type, listEty cty.Type, unsafe bool) conversion {
136 tupleEtys := tupleType.TupleElementTypes()
137
138 if len(tupleEtys) == 0 {
139 // Empty tuple short-circuit
140 return func(val cty.Value, path cty.Path) (cty.Value, error) {
141 return cty.SetValEmpty(listEty), nil
142 }
143 }
144
145 if listEty == cty.DynamicPseudoType {
146 // This is a special case where the caller wants us to find
147 // a suitable single type that all elements can convert to, if
148 // possible.
149 listEty, _ = unify(tupleEtys, unsafe)
150 if listEty == cty.NilType {
151 return nil
152 }
153 }
154
155 elemConvs := make([]conversion, len(tupleEtys))
156 for i, tupleEty := range tupleEtys {
157 if tupleEty.Equals(listEty) {
158 // no conversion required
159 continue
160 }
161
162 elemConvs[i] = getConversion(tupleEty, listEty, unsafe)
163 if elemConvs[i] == nil {
164 // If any of our element conversions are impossible, then the our
165 // whole conversion is impossible.
166 return nil
167 }
168 }
169
170 // If we fall out here then a conversion is possible, using the
171 // element conversions in elemConvs
172 return func(val cty.Value, path cty.Path) (cty.Value, error) {
173 elems := make([]cty.Value, 0, len(elemConvs))
174 path = append(path, nil)
175 i := int64(0)
176 it := val.ElementIterator()
177 for it.Next() {
178 _, val := it.Element()
179 var err error
180
181 path[len(path)-1] = cty.IndexStep{
182 Key: cty.NumberIntVal(i),
183 }
184
185 conv := elemConvs[i]
186 if conv != nil {
187 val, err = conv(val, path)
188 if err != nil {
189 return cty.NilVal, err
190 }
191 }
192 elems = append(elems, val)
193
194 i++
195 }
196
197 return cty.SetVal(elems), nil
198 }
199}
200
87// conversionTupleToList returns a conversion that will take a value of the 201// conversionTupleToList returns a conversion that will take a value of the
88// given tuple type and return a list of the given element type. 202// given tuple type and return a list of the given element type.
89// 203//
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_object.go b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_object.go
new file mode 100644
index 0000000..62dabb8
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_object.go
@@ -0,0 +1,76 @@
1package convert
2
3import (
4 "github.com/zclconf/go-cty/cty"
5)
6
7// conversionObjectToObject returns a conversion that will make the input
8// object type conform to the output object type, if possible.
9//
10// Conversion is possible only if the output type is a subset of the input
11// type, meaning that each attribute of the output type has a corresponding
12// attribute in the input type where a recursive conversion is available.
13//
14// Shallow object conversions work the same for both safe and unsafe modes,
15// but the safety flag is passed on to recursive conversions and may thus
16// limit the above definition of "subset".
17func conversionObjectToObject(in, out cty.Type, unsafe bool) conversion {
18 inAtys := in.AttributeTypes()
19 outAtys := out.AttributeTypes()
20 attrConvs := make(map[string]conversion)
21
22 for name, outAty := range outAtys {
23 inAty, exists := inAtys[name]
24 if !exists {
25 // No conversion is available, then.
26 return nil
27 }
28
29 if inAty.Equals(outAty) {
30 // No conversion needed, but we'll still record the attribute
31 // in our map for later reference.
32 attrConvs[name] = nil
33 continue
34 }
35
36 attrConvs[name] = getConversion(inAty, outAty, unsafe)
37 if attrConvs[name] == nil {
38 // If a recursive conversion isn't available, then our top-level
39 // configuration is impossible too.
40 return nil
41 }
42 }
43
44 // If we get here then a conversion is possible, using the attribute
45 // conversions given in attrConvs.
46 return func(val cty.Value, path cty.Path) (cty.Value, error) {
47 attrVals := make(map[string]cty.Value, len(attrConvs))
48 path = append(path, nil)
49 pathStep := &path[len(path)-1]
50
51 for it := val.ElementIterator(); it.Next(); {
52 nameVal, val := it.Element()
53 var err error
54
55 name := nameVal.AsString()
56 *pathStep = cty.GetAttrStep{
57 Name: name,
58 }
59
60 conv, exists := attrConvs[name]
61 if !exists {
62 continue
63 }
64 if conv != nil {
65 val, err = conv(val, path)
66 if err != nil {
67 return cty.NilVal, err
68 }
69 }
70
71 attrVals[name] = val
72 }
73
74 return cty.ObjectVal(attrVals), nil
75 }
76}
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
index e563ee3..e0dbf49 100644
--- a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go
@@ -1,8 +1,6 @@
1package convert 1package convert
2 2
3import ( 3import (
4 "math/big"
5
6 "github.com/zclconf/go-cty/cty" 4 "github.com/zclconf/go-cty/cty"
7) 5)
8 6
@@ -30,11 +28,11 @@ var primitiveConversionsSafe = map[cty.Type]map[cty.Type]conversion{
30var primitiveConversionsUnsafe = map[cty.Type]map[cty.Type]conversion{ 28var primitiveConversionsUnsafe = map[cty.Type]map[cty.Type]conversion{
31 cty.String: { 29 cty.String: {
32 cty.Number: func(val cty.Value, path cty.Path) (cty.Value, error) { 30 cty.Number: func(val cty.Value, path cty.Path) (cty.Value, error) {
33 f, _, err := big.ParseFloat(val.AsString(), 10, 512, big.ToNearestEven) 31 v, err := cty.ParseNumberVal(val.AsString())
34 if err != nil { 32 if err != nil {
35 return cty.NilVal, path.NewErrorf("a number is required") 33 return cty.NilVal, path.NewErrorf("a number is required")
36 } 34 }
37 return cty.NumberVal(f), nil 35 return v, nil
38 }, 36 },
39 cty.Bool: func(val cty.Value, path cty.Path) (cty.Value, error) { 37 cty.Bool: func(val cty.Value, path cty.Path) (cty.Value, error) {
40 switch val.AsString() { 38 switch val.AsString() {
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_tuple.go b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_tuple.go
new file mode 100644
index 0000000..592980a
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_tuple.go
@@ -0,0 +1,71 @@
1package convert
2
3import (
4 "github.com/zclconf/go-cty/cty"
5)
6
7// conversionTupleToTuple returns a conversion that will make the input
8// tuple type conform to the output tuple type, if possible.
9//
10// Conversion is possible only if the two tuple types have the same number
11// of elements and the corresponding elements by index can be converted.
12//
13// Shallow tuple conversions work the same for both safe and unsafe modes,
14// but the safety flag is passed on to recursive conversions and may thus
15// limit which element type conversions are possible.
16func conversionTupleToTuple(in, out cty.Type, unsafe bool) conversion {
17 inEtys := in.TupleElementTypes()
18 outEtys := out.TupleElementTypes()
19
20 if len(inEtys) != len(outEtys) {
21 return nil // no conversion is possible
22 }
23
24 elemConvs := make([]conversion, len(inEtys))
25
26 for i, outEty := range outEtys {
27 inEty := inEtys[i]
28
29 if inEty.Equals(outEty) {
30 // No conversion needed, so we can leave this one nil.
31 continue
32 }
33
34 elemConvs[i] = getConversion(inEty, outEty, unsafe)
35 if elemConvs[i] == nil {
36 // If a recursive conversion isn't available, then our top-level
37 // configuration is impossible too.
38 return nil
39 }
40 }
41
42 // If we get here then a conversion is possible, using the element
43 // conversions given in elemConvs.
44 return func(val cty.Value, path cty.Path) (cty.Value, error) {
45 elemVals := make([]cty.Value, len(elemConvs))
46 path = append(path, nil)
47 pathStep := &path[len(path)-1]
48
49 i := 0
50 for it := val.ElementIterator(); it.Next(); i++ {
51 _, val := it.Element()
52 var err error
53
54 *pathStep = cty.IndexStep{
55 Key: cty.NumberIntVal(int64(i)),
56 }
57
58 conv := elemConvs[i]
59 if conv != nil {
60 val, err = conv(val, path)
61 if err != nil {
62 return cty.NilVal, err
63 }
64 }
65
66 elemVals[i] = val
67 }
68
69 return cty.TupleVal(elemVals), nil
70 }
71}
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/mismatch_msg.go b/vendor/github.com/zclconf/go-cty/cty/convert/mismatch_msg.go
new file mode 100644
index 0000000..581304e
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/mismatch_msg.go
@@ -0,0 +1,220 @@
1package convert
2
3import (
4 "bytes"
5 "fmt"
6 "sort"
7
8 "github.com/zclconf/go-cty/cty"
9)
10
11// MismatchMessage is a helper to return an English-language description of
12// the differences between got and want, phrased as a reason why got does
13// not conform to want.
14//
15// This function does not itself attempt conversion, and so it should generally
16// be used only after a conversion has failed, to report the conversion failure
17// to an English-speaking user. The result will be confusing got is actually
18// conforming to or convertable to want.
19//
20// The shorthand helper function Convert uses this function internally to
21// produce its error messages, so callers of that function do not need to
22// also use MismatchMessage.
23//
24// This function is similar to Type.TestConformance, but it is tailored to
25// describing conversion failures and so the messages it generates relate
26// specifically to the conversion rules implemented in this package.
27func MismatchMessage(got, want cty.Type) string {
28 switch {
29
30 case got.IsObjectType() && want.IsObjectType():
31 // If both types are object types then we may be able to say something
32 // about their respective attributes.
33 return mismatchMessageObjects(got, want)
34
35 case got.IsTupleType() && want.IsListType() && want.ElementType() == cty.DynamicPseudoType:
36 // If conversion from tuple to list failed then it's because we couldn't
37 // find a common type to convert all of the tuple elements to.
38 return "all list elements must have the same type"
39
40 case got.IsTupleType() && want.IsSetType() && want.ElementType() == cty.DynamicPseudoType:
41 // If conversion from tuple to set failed then it's because we couldn't
42 // find a common type to convert all of the tuple elements to.
43 return "all set elements must have the same type"
44
45 case got.IsObjectType() && want.IsMapType() && want.ElementType() == cty.DynamicPseudoType:
46 // If conversion from object to map failed then it's because we couldn't
47 // find a common type to convert all of the object attributes to.
48 return "all map elements must have the same type"
49
50 case (got.IsTupleType() || got.IsObjectType()) && want.IsCollectionType():
51 return mismatchMessageCollectionsFromStructural(got, want)
52
53 case got.IsCollectionType() && want.IsCollectionType():
54 return mismatchMessageCollectionsFromCollections(got, want)
55
56 default:
57 // If we have nothing better to say, we'll just state what was required.
58 return want.FriendlyNameForConstraint() + " required"
59 }
60}
61
62func mismatchMessageObjects(got, want cty.Type) string {
63 // Per our conversion rules, "got" is allowed to be a superset of "want",
64 // and so we'll produce error messages here under that assumption.
65 gotAtys := got.AttributeTypes()
66 wantAtys := want.AttributeTypes()
67
68 // If we find missing attributes then we'll report those in preference,
69 // but if not then we will report a maximum of one non-conforming
70 // attribute, just to keep our messages relatively terse.
71 // We'll also prefer to report a recursive type error from an _unsafe_
72 // conversion over a safe one, because these are subjectively more
73 // "serious".
74 var missingAttrs []string
75 var unsafeMismatchAttr string
76 var safeMismatchAttr string
77
78 for name, wantAty := range wantAtys {
79 gotAty, exists := gotAtys[name]
80 if !exists {
81 missingAttrs = append(missingAttrs, name)
82 continue
83 }
84
85 // We'll now try to convert these attributes in isolation and
86 // see if we have a nested conversion error to report.
87 // We'll try an unsafe conversion first, and then fall back on
88 // safe if unsafe is possible.
89
90 // If we already have an unsafe mismatch attr error then we won't bother
91 // hunting for another one.
92 if unsafeMismatchAttr != "" {
93 continue
94 }
95 if conv := GetConversionUnsafe(gotAty, wantAty); conv == nil {
96 unsafeMismatchAttr = fmt.Sprintf("attribute %q: %s", name, MismatchMessage(gotAty, wantAty))
97 }
98
99 // If we already have a safe mismatch attr error then we won't bother
100 // hunting for another one.
101 if safeMismatchAttr != "" {
102 continue
103 }
104 if conv := GetConversion(gotAty, wantAty); conv == nil {
105 safeMismatchAttr = fmt.Sprintf("attribute %q: %s", name, MismatchMessage(gotAty, wantAty))
106 }
107 }
108
109 // We should now have collected at least one problem. If we have more than
110 // one then we'll use our preference order to decide what is most important
111 // to report.
112 switch {
113
114 case len(missingAttrs) != 0:
115 sort.Strings(missingAttrs)
116 switch len(missingAttrs) {
117 case 1:
118 return fmt.Sprintf("attribute %q is required", missingAttrs[0])
119 case 2:
120 return fmt.Sprintf("attributes %q and %q are required", missingAttrs[0], missingAttrs[1])
121 default:
122 sort.Strings(missingAttrs)
123 var buf bytes.Buffer
124 for _, name := range missingAttrs[:len(missingAttrs)-1] {
125 fmt.Fprintf(&buf, "%q, ", name)
126 }
127 fmt.Fprintf(&buf, "and %q", missingAttrs[len(missingAttrs)-1])
128 return fmt.Sprintf("attributes %s are required", buf.Bytes())
129 }
130
131 case unsafeMismatchAttr != "":
132 return unsafeMismatchAttr
133
134 case safeMismatchAttr != "":
135 return safeMismatchAttr
136
137 default:
138 // We should never get here, but if we do then we'll return
139 // just a generic message.
140 return "incorrect object attributes"
141 }
142}
143
144func mismatchMessageCollectionsFromStructural(got, want cty.Type) string {
145 // First some straightforward cases where the kind is just altogether wrong.
146 switch {
147 case want.IsListType() && !got.IsTupleType():
148 return want.FriendlyNameForConstraint() + " required"
149 case want.IsSetType() && !got.IsTupleType():
150 return want.FriendlyNameForConstraint() + " required"
151 case want.IsMapType() && !got.IsObjectType():
152 return want.FriendlyNameForConstraint() + " required"
153 }
154
155 // If the kinds are matched well enough then we'll move on to checking
156 // individual elements.
157 wantEty := want.ElementType()
158 switch {
159 case got.IsTupleType():
160 for i, gotEty := range got.TupleElementTypes() {
161 if gotEty.Equals(wantEty) {
162 continue // exact match, so no problem
163 }
164 if conv := getConversion(gotEty, wantEty, true); conv != nil {
165 continue // conversion is available, so no problem
166 }
167 return fmt.Sprintf("element %d: %s", i, MismatchMessage(gotEty, wantEty))
168 }
169
170 // If we get down here then something weird is going on but we'll
171 // return a reasonable fallback message anyway.
172 return fmt.Sprintf("all elements must be %s", wantEty.FriendlyNameForConstraint())
173
174 case got.IsObjectType():
175 for name, gotAty := range got.AttributeTypes() {
176 if gotAty.Equals(wantEty) {
177 continue // exact match, so no problem
178 }
179 if conv := getConversion(gotAty, wantEty, true); conv != nil {
180 continue // conversion is available, so no problem
181 }
182 return fmt.Sprintf("element %q: %s", name, MismatchMessage(gotAty, wantEty))
183 }
184
185 // If we get down here then something weird is going on but we'll
186 // return a reasonable fallback message anyway.
187 return fmt.Sprintf("all elements must be %s", wantEty.FriendlyNameForConstraint())
188
189 default:
190 // Should not be possible to get here since we only call this function
191 // with got as structural types, but...
192 return want.FriendlyNameForConstraint() + " required"
193 }
194}
195
196func mismatchMessageCollectionsFromCollections(got, want cty.Type) string {
197 // First some straightforward cases where the kind is just altogether wrong.
198 switch {
199 case want.IsListType() && !(got.IsListType() || got.IsSetType()):
200 return want.FriendlyNameForConstraint() + " required"
201 case want.IsSetType() && !(got.IsListType() || got.IsSetType()):
202 return want.FriendlyNameForConstraint() + " required"
203 case want.IsMapType() && !got.IsMapType():
204 return want.FriendlyNameForConstraint() + " required"
205 }
206
207 // If the kinds are matched well enough then we'll check the element types.
208 gotEty := got.ElementType()
209 wantEty := want.ElementType()
210 noun := "element type"
211 switch {
212 case want.IsListType():
213 noun = "list element type"
214 case want.IsSetType():
215 noun = "set element type"
216 case want.IsMapType():
217 noun = "map element type"
218 }
219 return fmt.Sprintf("incorrect %s: %s", noun, MismatchMessage(gotEty, wantEty))
220}
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/public.go b/vendor/github.com/zclconf/go-cty/cty/convert/public.go
index 55f44ae..af19bdc 100644
--- a/vendor/github.com/zclconf/go-cty/cty/convert/public.go
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/public.go
@@ -1,7 +1,7 @@
1package convert 1package convert
2 2
3import ( 3import (
4 "fmt" 4 "errors"
5 5
6 "github.com/zclconf/go-cty/cty" 6 "github.com/zclconf/go-cty/cty"
7) 7)
@@ -46,7 +46,7 @@ func Convert(in cty.Value, want cty.Type) (cty.Value, error) {
46 46
47 conv := GetConversionUnsafe(in.Type(), want) 47 conv := GetConversionUnsafe(in.Type(), want)
48 if conv == nil { 48 if conv == nil {
49 return cty.NilVal, fmt.Errorf("incorrect type; %s required", want.FriendlyName()) 49 return cty.NilVal, errors.New(MismatchMessage(in.Type(), want))
50 } 50 }
51 return conv(in) 51 return conv(in)
52} 52}
diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/unify.go b/vendor/github.com/zclconf/go-cty/cty/convert/unify.go
index bd6736b..53ebbfe 100644
--- a/vendor/github.com/zclconf/go-cty/cty/convert/unify.go
+++ b/vendor/github.com/zclconf/go-cty/cty/convert/unify.go
@@ -21,6 +21,39 @@ func unify(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
21 return cty.NilType, nil 21 return cty.NilType, nil
22 } 22 }
23 23
24 // If all of the given types are of the same structural kind, we may be
25 // able to construct a new type that they can all be unified to, even if
26 // that is not one of the given types. We must try this before the general
27 // behavior below because in unsafe mode we can convert an object type to
28 // a subset of that type, which would be a much less useful conversion for
29 // unification purposes.
30 {
31 objectCt := 0
32 tupleCt := 0
33 dynamicCt := 0
34 for _, ty := range types {
35 switch {
36 case ty.IsObjectType():
37 objectCt++
38 case ty.IsTupleType():
39 tupleCt++
40 case ty == cty.DynamicPseudoType:
41 dynamicCt++
42 default:
43 break
44 }
45 }
46 switch {
47 case objectCt > 0 && (objectCt+dynamicCt) == len(types):
48 return unifyObjectTypes(types, unsafe, dynamicCt > 0)
49 case tupleCt > 0 && (tupleCt+dynamicCt) == len(types):
50 return unifyTupleTypes(types, unsafe, dynamicCt > 0)
51 case objectCt > 0 && tupleCt > 0:
52 // Can never unify object and tuple types since they have incompatible kinds
53 return cty.NilType, nil
54 }
55 }
56
24 prefOrder := sortTypes(types) 57 prefOrder := sortTypes(types)
25 58
26 // sortTypes gives us an order where earlier items are preferable as 59 // sortTypes gives us an order where earlier items are preferable as
@@ -58,9 +91,224 @@ Preferences:
58 return wantType, conversions 91 return wantType, conversions
59 } 92 }
60 93
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 94 // If we fall out here, no unification is possible
65 return cty.NilType, nil 95 return cty.NilType, nil
66} 96}
97
98func unifyObjectTypes(types []cty.Type, unsafe bool, hasDynamic bool) (cty.Type, []Conversion) {
99 // If we had any dynamic types in the input here then we can't predict
100 // what path we'll take through here once these become known types, so
101 // we'll conservatively produce DynamicVal for these.
102 if hasDynamic {
103 return unifyAllAsDynamic(types)
104 }
105
106 // There are two different ways we can succeed here:
107 // - If all of the given object types have the same set of attribute names
108 // and the corresponding types are all unifyable, then we construct that
109 // type.
110 // - If the given object types have different attribute names or their
111 // corresponding types are not unifyable, we'll instead try to unify
112 // all of the attribute types together to produce a map type.
113 //
114 // Our unification behavior is intentionally stricter than our conversion
115 // behavior for subset object types because user intent is different with
116 // unification use-cases: it makes sense to allow {"foo":true} to convert
117 // to emptyobjectval, but unifying an object with an attribute with the
118 // empty object type should be an error because unifying to the empty
119 // object type would be suprising and useless.
120
121 firstAttrs := types[0].AttributeTypes()
122 for _, ty := range types[1:] {
123 thisAttrs := ty.AttributeTypes()
124 if len(thisAttrs) != len(firstAttrs) {
125 // If number of attributes is different then there can be no
126 // object type in common.
127 return unifyObjectTypesToMap(types, unsafe)
128 }
129 for name := range thisAttrs {
130 if _, ok := firstAttrs[name]; !ok {
131 // If attribute names don't exactly match then there can be
132 // no object type in common.
133 return unifyObjectTypesToMap(types, unsafe)
134 }
135 }
136 }
137
138 // If we get here then we've proven that all of the given object types
139 // have exactly the same set of attribute names, though the types may
140 // differ.
141 retAtys := make(map[string]cty.Type)
142 atysAcross := make([]cty.Type, len(types))
143 for name := range firstAttrs {
144 for i, ty := range types {
145 atysAcross[i] = ty.AttributeType(name)
146 }
147 retAtys[name], _ = unify(atysAcross, unsafe)
148 if retAtys[name] == cty.NilType {
149 // Cannot unify this attribute alone, which means that unification
150 // of everything down to a map type can't be possible either.
151 return cty.NilType, nil
152 }
153 }
154 retTy := cty.Object(retAtys)
155
156 conversions := make([]Conversion, len(types))
157 for i, ty := range types {
158 if ty.Equals(retTy) {
159 continue
160 }
161 if unsafe {
162 conversions[i] = GetConversionUnsafe(ty, retTy)
163 } else {
164 conversions[i] = GetConversion(ty, retTy)
165 }
166 if conversions[i] == nil {
167 // Shouldn't be reachable, since we were able to unify
168 return unifyObjectTypesToMap(types, unsafe)
169 }
170 }
171
172 return retTy, conversions
173}
174
175func unifyObjectTypesToMap(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
176 // This is our fallback case for unifyObjectTypes, where we see if we can
177 // construct a map type that can accept all of the attribute types.
178
179 var atys []cty.Type
180 for _, ty := range types {
181 for _, aty := range ty.AttributeTypes() {
182 atys = append(atys, aty)
183 }
184 }
185
186 ety, _ := unify(atys, unsafe)
187 if ety == cty.NilType {
188 return cty.NilType, nil
189 }
190
191 retTy := cty.Map(ety)
192 conversions := make([]Conversion, len(types))
193 for i, ty := range types {
194 if ty.Equals(retTy) {
195 continue
196 }
197 if unsafe {
198 conversions[i] = GetConversionUnsafe(ty, retTy)
199 } else {
200 conversions[i] = GetConversion(ty, retTy)
201 }
202 if conversions[i] == nil {
203 return cty.NilType, nil
204 }
205 }
206 return retTy, conversions
207}
208
209func unifyTupleTypes(types []cty.Type, unsafe bool, hasDynamic bool) (cty.Type, []Conversion) {
210 // If we had any dynamic types in the input here then we can't predict
211 // what path we'll take through here once these become known types, so
212 // we'll conservatively produce DynamicVal for these.
213 if hasDynamic {
214 return unifyAllAsDynamic(types)
215 }
216
217 // There are two different ways we can succeed here:
218 // - If all of the given tuple types have the same sequence of element types
219 // and the corresponding types are all unifyable, then we construct that
220 // type.
221 // - If the given tuple types have different element types or their
222 // corresponding types are not unifyable, we'll instead try to unify
223 // all of the elements types together to produce a list type.
224
225 firstEtys := types[0].TupleElementTypes()
226 for _, ty := range types[1:] {
227 thisEtys := ty.TupleElementTypes()
228 if len(thisEtys) != len(firstEtys) {
229 // If number of elements is different then there can be no
230 // tuple type in common.
231 return unifyTupleTypesToList(types, unsafe)
232 }
233 }
234
235 // If we get here then we've proven that all of the given tuple types
236 // have the same number of elements, though the types may differ.
237 retEtys := make([]cty.Type, len(firstEtys))
238 atysAcross := make([]cty.Type, len(types))
239 for idx := range firstEtys {
240 for tyI, ty := range types {
241 atysAcross[tyI] = ty.TupleElementTypes()[idx]
242 }
243 retEtys[idx], _ = unify(atysAcross, unsafe)
244 if retEtys[idx] == cty.NilType {
245 // Cannot unify this element alone, which means that unification
246 // of everything down to a map type can't be possible either.
247 return cty.NilType, nil
248 }
249 }
250 retTy := cty.Tuple(retEtys)
251
252 conversions := make([]Conversion, len(types))
253 for i, ty := range types {
254 if ty.Equals(retTy) {
255 continue
256 }
257 if unsafe {
258 conversions[i] = GetConversionUnsafe(ty, retTy)
259 } else {
260 conversions[i] = GetConversion(ty, retTy)
261 }
262 if conversions[i] == nil {
263 // Shouldn't be reachable, since we were able to unify
264 return unifyTupleTypesToList(types, unsafe)
265 }
266 }
267
268 return retTy, conversions
269}
270
271func unifyTupleTypesToList(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
272 // This is our fallback case for unifyTupleTypes, where we see if we can
273 // construct a list type that can accept all of the element types.
274
275 var etys []cty.Type
276 for _, ty := range types {
277 for _, ety := range ty.TupleElementTypes() {
278 etys = append(etys, ety)
279 }
280 }
281
282 ety, _ := unify(etys, unsafe)
283 if ety == cty.NilType {
284 return cty.NilType, nil
285 }
286
287 retTy := cty.List(ety)
288 conversions := make([]Conversion, len(types))
289 for i, ty := range types {
290 if ty.Equals(retTy) {
291 continue
292 }
293 if unsafe {
294 conversions[i] = GetConversionUnsafe(ty, retTy)
295 } else {
296 conversions[i] = GetConversion(ty, retTy)
297 }
298 if conversions[i] == nil {
299 // Shouldn't be reachable, since we were able to unify
300 return unifyObjectTypesToMap(types, unsafe)
301 }
302 }
303 return retTy, conversions
304}
305
306func unifyAllAsDynamic(types []cty.Type) (cty.Type, []Conversion) {
307 conversions := make([]Conversion, len(types))
308 for i := range conversions {
309 conversions[i] = func(cty.Value) (cty.Value, error) {
310 return cty.DynamicVal, nil
311 }
312 }
313 return cty.DynamicPseudoType, conversions
314}