]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/zclconf/go-cty/cty/type_conform.go
deps: github.com/hashicorp/terraform@sdk-v0.11-with-go-modules
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / zclconf / go-cty / cty / type_conform.go
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 }