mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-18 17:19:58 +00:00
143 lines
4.1 KiB
Go
143 lines
4.1 KiB
Go
|
package cty
|
||
|
|
||
|
// TestConformance recursively walks the receiver and the given other type and
|
||
|
// returns nil if the receiver *conforms* to the given type.
|
||
|
//
|
||
|
// Type conformance is similar to type equality but has one crucial difference:
|
||
|
// PseudoTypeDynamic can be used within the given type to represent that
|
||
|
// *any* type is allowed.
|
||
|
//
|
||
|
// If any non-conformities are found, the returned slice will be non-nil and
|
||
|
// contain at least one error value. It will be nil if the type is entirely
|
||
|
// conformant.
|
||
|
//
|
||
|
// Note that the special behavior of PseudoTypeDynamic is the *only* exception
|
||
|
// to normal type equality. Calling applications may wish to apply their own
|
||
|
// automatic conversion logic to the given data structure to create a more
|
||
|
// liberal notion of conformance to a type.
|
||
|
//
|
||
|
// Returned errors are usually (but not always) PathError instances that
|
||
|
// indicate where in the structure the error was found. If a returned error
|
||
|
// is of that type then the error message is written for (English-speaking)
|
||
|
// end-users working within the cty type system, not mentioning any Go-oriented
|
||
|
// implementation details.
|
||
|
func (t Type) TestConformance(other Type) []error {
|
||
|
path := make(Path, 0)
|
||
|
var errs []error
|
||
|
testConformance(t, other, path, &errs)
|
||
|
return errs
|
||
|
}
|
||
|
|
||
|
func testConformance(given Type, want Type, path Path, errs *[]error) {
|
||
|
if want.Equals(DynamicPseudoType) {
|
||
|
// anything goes!
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if given.Equals(want) {
|
||
|
// Any equal types are always conformant
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// The remainder of this function is concerned with detecting
|
||
|
// and reporting the specific non-conformance, since we wouldn't
|
||
|
// have got here if the types were not divergent.
|
||
|
// We treat compound structures as special so that we can report
|
||
|
// specifically what is non-conforming, rather than simply returning
|
||
|
// the entire type names and letting the user puzzle it out.
|
||
|
|
||
|
if given.IsObjectType() && want.IsObjectType() {
|
||
|
givenAttrs := given.AttributeTypes()
|
||
|
wantAttrs := want.AttributeTypes()
|
||
|
|
||
|
if len(givenAttrs) != len(wantAttrs) {
|
||
|
// Something is missing from one of them.
|
||
|
for k := range givenAttrs {
|
||
|
if _, exists := wantAttrs[k]; !exists {
|
||
|
*errs = append(
|
||
|
*errs,
|
||
|
errorf(path, "unsupported attribute %q", k),
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
for k := range wantAttrs {
|
||
|
if _, exists := givenAttrs[k]; !exists {
|
||
|
*errs = append(
|
||
|
*errs,
|
||
|
errorf(path, "missing required attribute %q", k),
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
path = append(path, nil)
|
||
|
pathIdx := len(path) - 1
|
||
|
|
||
|
for k, wantAttrType := range wantAttrs {
|
||
|
if givenAttrType, exists := givenAttrs[k]; exists {
|
||
|
path[pathIdx] = GetAttrStep{Name: k}
|
||
|
testConformance(givenAttrType, wantAttrType, path, errs)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
path = path[0:pathIdx]
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if given.IsTupleType() && want.IsTupleType() {
|
||
|
givenElems := given.TupleElementTypes()
|
||
|
wantElems := want.TupleElementTypes()
|
||
|
|
||
|
if len(givenElems) != len(wantElems) {
|
||
|
*errs = append(
|
||
|
*errs,
|
||
|
errorf(path, "%d elements are required, but got %d", len(wantElems), len(givenElems)),
|
||
|
)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
path = append(path, nil)
|
||
|
pathIdx := len(path) - 1
|
||
|
|
||
|
for i, wantElemType := range wantElems {
|
||
|
givenElemType := givenElems[i]
|
||
|
path[pathIdx] = IndexStep{Key: NumberIntVal(int64(i))}
|
||
|
testConformance(givenElemType, wantElemType, path, errs)
|
||
|
}
|
||
|
|
||
|
path = path[0:pathIdx]
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if given.IsListType() && want.IsListType() {
|
||
|
path = append(path, IndexStep{Key: UnknownVal(Number)})
|
||
|
pathIdx := len(path) - 1
|
||
|
testConformance(given.ElementType(), want.ElementType(), path, errs)
|
||
|
path = path[0:pathIdx]
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if given.IsMapType() && want.IsMapType() {
|
||
|
path = append(path, IndexStep{Key: UnknownVal(String)})
|
||
|
pathIdx := len(path) - 1
|
||
|
testConformance(given.ElementType(), want.ElementType(), path, errs)
|
||
|
path = path[0:pathIdx]
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if given.IsSetType() && want.IsSetType() {
|
||
|
path = append(path, IndexStep{Key: UnknownVal(given.ElementType())})
|
||
|
pathIdx := len(path) - 1
|
||
|
testConformance(given.ElementType(), want.ElementType(), path, errs)
|
||
|
path = path[0:pathIdx]
|
||
|
return
|
||
|
}
|
||
|
|
||
|
*errs = append(
|
||
|
*errs,
|
||
|
errorf(path, "%s required, but received %s", want.FriendlyName(), given.FriendlyName()),
|
||
|
)
|
||
|
}
|