diff options
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/type_conform.go')
-rw-r--r-- | vendor/github.com/zclconf/go-cty/cty/type_conform.go | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/type_conform.go b/vendor/github.com/zclconf/go-cty/cty/type_conform.go new file mode 100644 index 0000000..b417dc7 --- /dev/null +++ b/vendor/github.com/zclconf/go-cty/cty/type_conform.go | |||
@@ -0,0 +1,142 @@ | |||
1 | package cty | ||
2 | |||
3 | // TestConformance recursively walks the receiver and the given other type and | ||
4 | // returns nil if the receiver *conforms* to the given type. | ||
5 | // | ||
6 | // Type conformance is similar to type equality but has one crucial difference: | ||
7 | // PseudoTypeDynamic can be used within the given type to represent that | ||
8 | // *any* type is allowed. | ||
9 | // | ||
10 | // If any non-conformities are found, the returned slice will be non-nil and | ||
11 | // contain at least one error value. It will be nil if the type is entirely | ||
12 | // conformant. | ||
13 | // | ||
14 | // Note that the special behavior of PseudoTypeDynamic is the *only* exception | ||
15 | // to normal type equality. Calling applications may wish to apply their own | ||
16 | // automatic conversion logic to the given data structure to create a more | ||
17 | // liberal notion of conformance to a type. | ||
18 | // | ||
19 | // Returned errors are usually (but not always) PathError instances that | ||
20 | // indicate where in the structure the error was found. If a returned error | ||
21 | // is of that type then the error message is written for (English-speaking) | ||
22 | // end-users working within the cty type system, not mentioning any Go-oriented | ||
23 | // implementation details. | ||
24 | func (t Type) TestConformance(other Type) []error { | ||
25 | path := make(Path, 0) | ||
26 | var errs []error | ||
27 | testConformance(t, other, path, &errs) | ||
28 | return errs | ||
29 | } | ||
30 | |||
31 | func testConformance(given Type, want Type, path Path, errs *[]error) { | ||
32 | if want.Equals(DynamicPseudoType) { | ||
33 | // anything goes! | ||
34 | return | ||
35 | } | ||
36 | |||
37 | if given.Equals(want) { | ||
38 | // Any equal types are always conformant | ||
39 | return | ||
40 | } | ||
41 | |||
42 | // The remainder of this function is concerned with detecting | ||
43 | // and reporting the specific non-conformance, since we wouldn't | ||
44 | // have got here if the types were not divergent. | ||
45 | // We treat compound structures as special so that we can report | ||
46 | // specifically what is non-conforming, rather than simply returning | ||
47 | // the entire type names and letting the user puzzle it out. | ||
48 | |||
49 | if given.IsObjectType() && want.IsObjectType() { | ||
50 | givenAttrs := given.AttributeTypes() | ||
51 | wantAttrs := want.AttributeTypes() | ||
52 | |||
53 | if len(givenAttrs) != len(wantAttrs) { | ||
54 | // Something is missing from one of them. | ||
55 | for k := range givenAttrs { | ||
56 | if _, exists := wantAttrs[k]; !exists { | ||
57 | *errs = append( | ||
58 | *errs, | ||
59 | errorf(path, "unsupported attribute %q", k), | ||
60 | ) | ||
61 | } | ||
62 | } | ||
63 | for k := range wantAttrs { | ||
64 | if _, exists := givenAttrs[k]; !exists { | ||
65 | *errs = append( | ||
66 | *errs, | ||
67 | errorf(path, "missing required attribute %q", k), | ||
68 | ) | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | path = append(path, nil) | ||
74 | pathIdx := len(path) - 1 | ||
75 | |||
76 | for k, wantAttrType := range wantAttrs { | ||
77 | if givenAttrType, exists := givenAttrs[k]; exists { | ||
78 | path[pathIdx] = GetAttrStep{Name: k} | ||
79 | testConformance(givenAttrType, wantAttrType, path, errs) | ||
80 | } | ||
81 | } | ||
82 | |||
83 | path = path[0:pathIdx] | ||
84 | |||
85 | return | ||
86 | } | ||
87 | |||
88 | if given.IsTupleType() && want.IsTupleType() { | ||
89 | givenElems := given.TupleElementTypes() | ||
90 | wantElems := want.TupleElementTypes() | ||
91 | |||
92 | if len(givenElems) != len(wantElems) { | ||
93 | *errs = append( | ||
94 | *errs, | ||
95 | errorf(path, "%d elements are required, but got %d", len(wantElems), len(givenElems)), | ||
96 | ) | ||
97 | return | ||
98 | } | ||
99 | |||
100 | path = append(path, nil) | ||
101 | pathIdx := len(path) - 1 | ||
102 | |||
103 | for i, wantElemType := range wantElems { | ||
104 | givenElemType := givenElems[i] | ||
105 | path[pathIdx] = IndexStep{Key: NumberIntVal(int64(i))} | ||
106 | testConformance(givenElemType, wantElemType, path, errs) | ||
107 | } | ||
108 | |||
109 | path = path[0:pathIdx] | ||
110 | |||
111 | return | ||
112 | } | ||
113 | |||
114 | if given.IsListType() && want.IsListType() { | ||
115 | path = append(path, IndexStep{Key: UnknownVal(Number)}) | ||
116 | pathIdx := len(path) - 1 | ||
117 | testConformance(given.ElementType(), want.ElementType(), path, errs) | ||
118 | path = path[0:pathIdx] | ||
119 | return | ||
120 | } | ||
121 | |||
122 | if given.IsMapType() && want.IsMapType() { | ||
123 | path = append(path, IndexStep{Key: UnknownVal(String)}) | ||
124 | pathIdx := len(path) - 1 | ||
125 | testConformance(given.ElementType(), want.ElementType(), path, errs) | ||
126 | path = path[0:pathIdx] | ||
127 | return | ||
128 | } | ||
129 | |||
130 | if given.IsSetType() && want.IsSetType() { | ||
131 | path = append(path, IndexStep{Key: UnknownVal(given.ElementType())}) | ||
132 | pathIdx := len(path) - 1 | ||
133 | testConformance(given.ElementType(), want.ElementType(), path, errs) | ||
134 | path = path[0:pathIdx] | ||
135 | return | ||
136 | } | ||
137 | |||
138 | *errs = append( | ||
139 | *errs, | ||
140 | errorf(path, "%s required, but received %s", want.FriendlyName(), given.FriendlyName()), | ||
141 | ) | ||
142 | } | ||