mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-19 01:29:57 +00:00
166 lines
4.1 KiB
Go
166 lines
4.1 KiB
Go
|
package convert
|
||
|
|
||
|
import (
|
||
|
"github.com/zclconf/go-cty/cty"
|
||
|
)
|
||
|
|
||
|
// compareTypes implements a preference order for unification.
|
||
|
//
|
||
|
// The result of this method is not useful for anything other than unification
|
||
|
// preferences, since it assumes that the caller will verify that any suggested
|
||
|
// conversion is actually possible and it is thus able to to make certain
|
||
|
// optimistic assumptions.
|
||
|
func compareTypes(a cty.Type, b cty.Type) int {
|
||
|
|
||
|
// DynamicPseudoType always has lowest preference, because anything can
|
||
|
// convert to it (it acts as a placeholder for "any type") and we want
|
||
|
// to optimistically assume that any dynamics will converge on matching
|
||
|
// their neighbors.
|
||
|
if a == cty.DynamicPseudoType || b == cty.DynamicPseudoType {
|
||
|
if a != cty.DynamicPseudoType {
|
||
|
return -1
|
||
|
}
|
||
|
if b != cty.DynamicPseudoType {
|
||
|
return 1
|
||
|
}
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
if a.IsPrimitiveType() && b.IsPrimitiveType() {
|
||
|
// String is a supertype of all primitive types, because we can
|
||
|
// represent all primitive values as specially-formatted strings.
|
||
|
if a == cty.String || b == cty.String {
|
||
|
if a != cty.String {
|
||
|
return 1
|
||
|
}
|
||
|
if b != cty.String {
|
||
|
return -1
|
||
|
}
|
||
|
return 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if a.IsListType() && b.IsListType() {
|
||
|
return compareTypes(a.ElementType(), b.ElementType())
|
||
|
}
|
||
|
if a.IsSetType() && b.IsSetType() {
|
||
|
return compareTypes(a.ElementType(), b.ElementType())
|
||
|
}
|
||
|
if a.IsMapType() && b.IsMapType() {
|
||
|
return compareTypes(a.ElementType(), b.ElementType())
|
||
|
}
|
||
|
|
||
|
// From this point on we may have swapped the two items in order to
|
||
|
// simplify our cases. Therefore any non-zero return after this point
|
||
|
// must be multiplied by "swap" to potentially invert the return value
|
||
|
// if needed.
|
||
|
swap := 1
|
||
|
switch {
|
||
|
case a.IsTupleType() && b.IsListType():
|
||
|
fallthrough
|
||
|
case a.IsObjectType() && b.IsMapType():
|
||
|
fallthrough
|
||
|
case a.IsSetType() && b.IsTupleType():
|
||
|
fallthrough
|
||
|
case a.IsSetType() && b.IsListType():
|
||
|
a, b = b, a
|
||
|
swap = -1
|
||
|
}
|
||
|
|
||
|
if b.IsSetType() && (a.IsTupleType() || a.IsListType()) {
|
||
|
// We'll just optimistically assume that the element types are
|
||
|
// unifyable/convertible, and let a second recursive pass
|
||
|
// figure out how to make that so.
|
||
|
return -1 * swap
|
||
|
}
|
||
|
|
||
|
if a.IsListType() && b.IsTupleType() {
|
||
|
// We'll just optimistically assume that the tuple's element types
|
||
|
// can be unified into something compatible with the list's element
|
||
|
// type.
|
||
|
return -1 * swap
|
||
|
}
|
||
|
|
||
|
if a.IsMapType() && b.IsObjectType() {
|
||
|
// We'll just optimistically assume that the object's attribute types
|
||
|
// can be unified into something compatible with the map's element
|
||
|
// type.
|
||
|
return -1 * swap
|
||
|
}
|
||
|
|
||
|
// For object and tuple types, comparing two types doesn't really tell
|
||
|
// the whole story because it may be possible to construct a new type C
|
||
|
// that is the supertype of both A and B by unifying each attribute/element
|
||
|
// separately. That possibility is handled by Unify as a follow-up if
|
||
|
// type sorting is insufficient to produce a valid result.
|
||
|
//
|
||
|
// Here we will take care of the simple possibilities where no new type
|
||
|
// is needed.
|
||
|
if a.IsObjectType() && b.IsObjectType() {
|
||
|
atysA := a.AttributeTypes()
|
||
|
atysB := b.AttributeTypes()
|
||
|
|
||
|
if len(atysA) != len(atysB) {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
hasASuper := false
|
||
|
hasBSuper := false
|
||
|
for k := range atysA {
|
||
|
if _, has := atysB[k]; !has {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
cmp := compareTypes(atysA[k], atysB[k])
|
||
|
if cmp < 0 {
|
||
|
hasASuper = true
|
||
|
} else if cmp > 0 {
|
||
|
hasBSuper = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case hasASuper && hasBSuper:
|
||
|
return 0
|
||
|
case hasASuper:
|
||
|
return -1 * swap
|
||
|
case hasBSuper:
|
||
|
return 1 * swap
|
||
|
default:
|
||
|
return 0
|
||
|
}
|
||
|
}
|
||
|
if a.IsTupleType() && b.IsTupleType() {
|
||
|
etysA := a.TupleElementTypes()
|
||
|
etysB := b.TupleElementTypes()
|
||
|
|
||
|
if len(etysA) != len(etysB) {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
hasASuper := false
|
||
|
hasBSuper := false
|
||
|
for i := range etysA {
|
||
|
cmp := compareTypes(etysA[i], etysB[i])
|
||
|
if cmp < 0 {
|
||
|
hasASuper = true
|
||
|
} else if cmp > 0 {
|
||
|
hasBSuper = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
case hasASuper && hasBSuper:
|
||
|
return 0
|
||
|
case hasASuper:
|
||
|
return -1 * swap
|
||
|
case hasBSuper:
|
||
|
return 1 * swap
|
||
|
default:
|
||
|
return 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0
|
||
|
}
|