diff options
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/convert')
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. | ||
93 | func 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. | ||
135 | func 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 @@ | |||
1 | package convert | ||
2 | |||
3 | import ( | ||
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". | ||
17 | func 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 @@ | |||
1 | package convert | 1 | package convert |
2 | 2 | ||
3 | import ( | 3 | import ( |
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{ | |||
30 | var primitiveConversionsUnsafe = map[cty.Type]map[cty.Type]conversion{ | 28 | var 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 @@ | |||
1 | package convert | ||
2 | |||
3 | import ( | ||
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. | ||
16 | func 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 @@ | |||
1 | package convert | ||
2 | |||
3 | import ( | ||
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. | ||
27 | func 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 | |||
62 | func 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 | |||
144 | func 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 | |||
196 | func 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 @@ | |||
1 | package convert | 1 | package convert |
2 | 2 | ||
3 | import ( | 3 | import ( |
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 | |||
98 | func 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 | |||
175 | func 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 | |||
209 | func 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 | |||
271 | func 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 | |||
306 | func 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 | } | ||