mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	
							
								
								
									
										165
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/compare_types.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/compare_types.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										181
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,181 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// conversion is an internal variant of Conversion that carries around
 | 
			
		||||
// a cty.Path to be used in error responses.
 | 
			
		||||
type conversion func(cty.Value, cty.Path) (cty.Value, error)
 | 
			
		||||
 | 
			
		||||
func getConversion(in cty.Type, out cty.Type, unsafe bool) conversion {
 | 
			
		||||
	conv := getConversionKnown(in, out, unsafe)
 | 
			
		||||
	if conv == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Wrap the conversion in some standard checks that we don't want to
 | 
			
		||||
	// have to repeat in every conversion function.
 | 
			
		||||
	var ret conversion
 | 
			
		||||
	ret = func(in cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		if in.IsMarked() {
 | 
			
		||||
			// We must unmark during the conversion and then re-apply the
 | 
			
		||||
			// same marks to the result.
 | 
			
		||||
			in, inMarks := in.Unmark()
 | 
			
		||||
			v, err := ret(in, path)
 | 
			
		||||
			if v != cty.NilVal {
 | 
			
		||||
				v = v.WithMarks(inMarks)
 | 
			
		||||
			}
 | 
			
		||||
			return v, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if out == cty.DynamicPseudoType {
 | 
			
		||||
			// Conversion to DynamicPseudoType always just passes through verbatim.
 | 
			
		||||
			return in, nil
 | 
			
		||||
		}
 | 
			
		||||
		if !in.IsKnown() {
 | 
			
		||||
			return cty.UnknownVal(out), nil
 | 
			
		||||
		}
 | 
			
		||||
		if in.IsNull() {
 | 
			
		||||
			// We'll pass through nulls, albeit type converted, and let
 | 
			
		||||
			// the caller deal with whatever handling they want to do in
 | 
			
		||||
			// case null values are considered valid in some applications.
 | 
			
		||||
			return cty.NullVal(out), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return conv(in, path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getConversionKnown(in cty.Type, out cty.Type, unsafe bool) conversion {
 | 
			
		||||
	switch {
 | 
			
		||||
 | 
			
		||||
	case out == cty.DynamicPseudoType:
 | 
			
		||||
		// Conversion *to* DynamicPseudoType means that the caller wishes
 | 
			
		||||
		// to allow any type in this position, so we'll produce a do-nothing
 | 
			
		||||
		// conversion that just passes through the value as-is.
 | 
			
		||||
		return dynamicPassthrough
 | 
			
		||||
 | 
			
		||||
	case unsafe && in == cty.DynamicPseudoType:
 | 
			
		||||
		// Conversion *from* DynamicPseudoType means that we have a value
 | 
			
		||||
		// whose type isn't yet known during type checking. For these we will
 | 
			
		||||
		// assume that conversion will succeed and deal with any errors that
 | 
			
		||||
		// result (which is why we can only do this when "unsafe" is set).
 | 
			
		||||
		return dynamicFixup(out)
 | 
			
		||||
 | 
			
		||||
	case in.IsPrimitiveType() && out.IsPrimitiveType():
 | 
			
		||||
		conv := primitiveConversionsSafe[in][out]
 | 
			
		||||
		if conv != nil {
 | 
			
		||||
			return conv
 | 
			
		||||
		}
 | 
			
		||||
		if unsafe {
 | 
			
		||||
			return primitiveConversionsUnsafe[in][out]
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
 | 
			
		||||
	case out.IsObjectType() && in.IsObjectType():
 | 
			
		||||
		return conversionObjectToObject(in, out, unsafe)
 | 
			
		||||
 | 
			
		||||
	case out.IsTupleType() && in.IsTupleType():
 | 
			
		||||
		return conversionTupleToTuple(in, out, unsafe)
 | 
			
		||||
 | 
			
		||||
	case out.IsListType() && (in.IsListType() || in.IsSetType()):
 | 
			
		||||
		inEty := in.ElementType()
 | 
			
		||||
		outEty := out.ElementType()
 | 
			
		||||
		if inEty.Equals(outEty) {
 | 
			
		||||
			// This indicates that we're converting from list to set with
 | 
			
		||||
			// the same element type, so we don't need an element converter.
 | 
			
		||||
			return conversionCollectionToList(outEty, nil)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		convEty := getConversion(inEty, outEty, unsafe)
 | 
			
		||||
		if convEty == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return conversionCollectionToList(outEty, convEty)
 | 
			
		||||
 | 
			
		||||
	case out.IsSetType() && (in.IsListType() || in.IsSetType()):
 | 
			
		||||
		if in.IsListType() && !unsafe {
 | 
			
		||||
			// Conversion from list to map is unsafe because it will lose
 | 
			
		||||
			// information: the ordering will not be preserved, and any
 | 
			
		||||
			// duplicate elements will be conflated.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		inEty := in.ElementType()
 | 
			
		||||
		outEty := out.ElementType()
 | 
			
		||||
		convEty := getConversion(inEty, outEty, unsafe)
 | 
			
		||||
		if inEty.Equals(outEty) {
 | 
			
		||||
			// This indicates that we're converting from set to list with
 | 
			
		||||
			// the same element type, so we don't need an element converter.
 | 
			
		||||
			return conversionCollectionToSet(outEty, nil)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if convEty == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return conversionCollectionToSet(outEty, convEty)
 | 
			
		||||
 | 
			
		||||
	case out.IsMapType() && in.IsMapType():
 | 
			
		||||
		inEty := in.ElementType()
 | 
			
		||||
		outEty := out.ElementType()
 | 
			
		||||
		convEty := getConversion(inEty, outEty, unsafe)
 | 
			
		||||
		if convEty == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return conversionCollectionToMap(outEty, convEty)
 | 
			
		||||
 | 
			
		||||
	case out.IsListType() && in.IsTupleType():
 | 
			
		||||
		outEty := out.ElementType()
 | 
			
		||||
		return conversionTupleToList(in, outEty, unsafe)
 | 
			
		||||
 | 
			
		||||
	case out.IsSetType() && in.IsTupleType():
 | 
			
		||||
		outEty := out.ElementType()
 | 
			
		||||
		return conversionTupleToSet(in, outEty, unsafe)
 | 
			
		||||
 | 
			
		||||
	case out.IsMapType() && in.IsObjectType():
 | 
			
		||||
		outEty := out.ElementType()
 | 
			
		||||
		return conversionObjectToMap(in, outEty, unsafe)
 | 
			
		||||
 | 
			
		||||
	case in.IsCapsuleType() || out.IsCapsuleType():
 | 
			
		||||
		if !unsafe {
 | 
			
		||||
			// Capsule types can only participate in "unsafe" conversions,
 | 
			
		||||
			// because we don't know enough about their conversion behaviors
 | 
			
		||||
			// to be sure that they will always be safe.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		if in.Equals(out) {
 | 
			
		||||
			// conversion to self is never allowed
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		if out.IsCapsuleType() {
 | 
			
		||||
			if fn := out.CapsuleOps().ConversionTo; fn != nil {
 | 
			
		||||
				return conversionToCapsule(in, out, fn)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if in.IsCapsuleType() {
 | 
			
		||||
			if fn := in.CapsuleOps().ConversionFrom; fn != nil {
 | 
			
		||||
				return conversionFromCapsule(in, out, fn)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// No conversion operation is available, then.
 | 
			
		||||
		return nil
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return nil
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// retConversion wraps a conversion (internal type) so it can be returned
 | 
			
		||||
// as a Conversion (public type).
 | 
			
		||||
func retConversion(conv conversion) Conversion {
 | 
			
		||||
	if conv == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(in cty.Value) (cty.Value, error) {
 | 
			
		||||
		return conv(in, cty.Path(nil))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_capsule.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_capsule.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func conversionToCapsule(inTy, outTy cty.Type, fn func(inTy cty.Type) func(cty.Value, cty.Path) (interface{}, error)) conversion {
 | 
			
		||||
	rawConv := fn(inTy)
 | 
			
		||||
	if rawConv == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(in cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		rawV, err := rawConv(in, path)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return cty.NilVal, err
 | 
			
		||||
		}
 | 
			
		||||
		return cty.CapsuleVal(outTy, rawV), nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func conversionFromCapsule(inTy, outTy cty.Type, fn func(outTy cty.Type) func(interface{}, cty.Path) (cty.Value, error)) conversion {
 | 
			
		||||
	rawConv := fn(outTy)
 | 
			
		||||
	if rawConv == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(in cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		return rawConv(in.EncapsulatedValue(), path)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										340
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										340
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,340 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// conversionCollectionToList returns a conversion that will apply the given
 | 
			
		||||
// conversion to all of the elements of a collection (something that supports
 | 
			
		||||
// ForEachElement and LengthInt) and then returns the result as a list.
 | 
			
		||||
//
 | 
			
		||||
// "conv" can be nil if the elements are expected to already be of the
 | 
			
		||||
// correct type and just need to be re-wrapped into a list. (For example,
 | 
			
		||||
// if we're converting from a set into a list of the same element type.)
 | 
			
		||||
func conversionCollectionToList(ety cty.Type, conv conversion) conversion {
 | 
			
		||||
	return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		elems := make([]cty.Value, 0, val.LengthInt())
 | 
			
		||||
		i := int64(0)
 | 
			
		||||
		path = append(path, nil)
 | 
			
		||||
		it := val.ElementIterator()
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			_, val := it.Element()
 | 
			
		||||
			var err error
 | 
			
		||||
 | 
			
		||||
			path[len(path)-1] = cty.IndexStep{
 | 
			
		||||
				Key: cty.NumberIntVal(i),
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if conv != nil {
 | 
			
		||||
				val, err = conv(val, path)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return cty.NilVal, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			elems = append(elems, val)
 | 
			
		||||
 | 
			
		||||
			i++
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(elems) == 0 {
 | 
			
		||||
			return cty.ListValEmpty(ety), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return cty.ListVal(elems), nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// conversionCollectionToSet returns a conversion that will apply the given
 | 
			
		||||
// conversion to all of the elements of a collection (something that supports
 | 
			
		||||
// ForEachElement and LengthInt) and then returns the result as a set.
 | 
			
		||||
//
 | 
			
		||||
// "conv" can be nil if the elements are expected to already be of the
 | 
			
		||||
// correct type and just need to be re-wrapped into a set. (For example,
 | 
			
		||||
// if we're converting from a list into a set of the same element type.)
 | 
			
		||||
func conversionCollectionToSet(ety cty.Type, conv conversion) conversion {
 | 
			
		||||
	return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		elems := make([]cty.Value, 0, val.LengthInt())
 | 
			
		||||
		i := int64(0)
 | 
			
		||||
		path = append(path, nil)
 | 
			
		||||
		it := val.ElementIterator()
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			_, val := it.Element()
 | 
			
		||||
			var err error
 | 
			
		||||
 | 
			
		||||
			path[len(path)-1] = cty.IndexStep{
 | 
			
		||||
				Key: cty.NumberIntVal(i),
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if conv != nil {
 | 
			
		||||
				val, err = conv(val, path)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return cty.NilVal, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			elems = append(elems, val)
 | 
			
		||||
 | 
			
		||||
			i++
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(elems) == 0 {
 | 
			
		||||
			return cty.SetValEmpty(ety), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return cty.SetVal(elems), nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// conversionCollectionToMap returns a conversion that will apply the given
 | 
			
		||||
// conversion to all of the elements of a collection (something that supports
 | 
			
		||||
// ForEachElement and LengthInt) and then returns the result as a map.
 | 
			
		||||
//
 | 
			
		||||
// "conv" can be nil if the elements are expected to already be of the
 | 
			
		||||
// correct type and just need to be re-wrapped into a map.
 | 
			
		||||
func conversionCollectionToMap(ety cty.Type, conv conversion) conversion {
 | 
			
		||||
	return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		elems := make(map[string]cty.Value, 0)
 | 
			
		||||
		path = append(path, nil)
 | 
			
		||||
		it := val.ElementIterator()
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			key, val := it.Element()
 | 
			
		||||
			var err error
 | 
			
		||||
 | 
			
		||||
			path[len(path)-1] = cty.IndexStep{
 | 
			
		||||
				Key: key,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			keyStr, err := Convert(key, cty.String)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				// Should never happen, because keys can only be numbers or
 | 
			
		||||
				// strings and both can convert to string.
 | 
			
		||||
				return cty.DynamicVal, path.NewErrorf("cannot convert key type %s to string for map", key.Type().FriendlyName())
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if conv != nil {
 | 
			
		||||
				val, err = conv(val, path)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return cty.NilVal, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			elems[keyStr.AsString()] = val
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(elems) == 0 {
 | 
			
		||||
			return cty.MapValEmpty(ety), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return cty.MapVal(elems), nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// conversionTupleToSet returns a conversion that will take a value of the
 | 
			
		||||
// given tuple type and return a set of the given element type.
 | 
			
		||||
//
 | 
			
		||||
// Will panic if the given tupleType isn't actually a tuple type.
 | 
			
		||||
func conversionTupleToSet(tupleType cty.Type, listEty cty.Type, unsafe bool) conversion {
 | 
			
		||||
	tupleEtys := tupleType.TupleElementTypes()
 | 
			
		||||
 | 
			
		||||
	if len(tupleEtys) == 0 {
 | 
			
		||||
		// Empty tuple short-circuit
 | 
			
		||||
		return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
			return cty.SetValEmpty(listEty), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if listEty == cty.DynamicPseudoType {
 | 
			
		||||
		// This is a special case where the caller wants us to find
 | 
			
		||||
		// a suitable single type that all elements can convert to, if
 | 
			
		||||
		// possible.
 | 
			
		||||
		listEty, _ = unify(tupleEtys, unsafe)
 | 
			
		||||
		if listEty == cty.NilType {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	elemConvs := make([]conversion, len(tupleEtys))
 | 
			
		||||
	for i, tupleEty := range tupleEtys {
 | 
			
		||||
		if tupleEty.Equals(listEty) {
 | 
			
		||||
			// no conversion required
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		elemConvs[i] = getConversion(tupleEty, listEty, unsafe)
 | 
			
		||||
		if elemConvs[i] == nil {
 | 
			
		||||
			// If any of our element conversions are impossible, then the our
 | 
			
		||||
			// whole conversion is impossible.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we fall out here then a conversion is possible, using the
 | 
			
		||||
	// element conversions in elemConvs
 | 
			
		||||
	return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		elems := make([]cty.Value, 0, len(elemConvs))
 | 
			
		||||
		path = append(path, nil)
 | 
			
		||||
		i := int64(0)
 | 
			
		||||
		it := val.ElementIterator()
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			_, val := it.Element()
 | 
			
		||||
			var err error
 | 
			
		||||
 | 
			
		||||
			path[len(path)-1] = cty.IndexStep{
 | 
			
		||||
				Key: cty.NumberIntVal(i),
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			conv := elemConvs[i]
 | 
			
		||||
			if conv != nil {
 | 
			
		||||
				val, err = conv(val, path)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return cty.NilVal, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			elems = append(elems, val)
 | 
			
		||||
 | 
			
		||||
			i++
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return cty.SetVal(elems), nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// conversionTupleToList returns a conversion that will take a value of the
 | 
			
		||||
// given tuple type and return a list of the given element type.
 | 
			
		||||
//
 | 
			
		||||
// Will panic if the given tupleType isn't actually a tuple type.
 | 
			
		||||
func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) conversion {
 | 
			
		||||
	tupleEtys := tupleType.TupleElementTypes()
 | 
			
		||||
 | 
			
		||||
	if len(tupleEtys) == 0 {
 | 
			
		||||
		// Empty tuple short-circuit
 | 
			
		||||
		return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
			return cty.ListValEmpty(listEty), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if listEty == cty.DynamicPseudoType {
 | 
			
		||||
		// This is a special case where the caller wants us to find
 | 
			
		||||
		// a suitable single type that all elements can convert to, if
 | 
			
		||||
		// possible.
 | 
			
		||||
		listEty, _ = unify(tupleEtys, unsafe)
 | 
			
		||||
		if listEty == cty.NilType {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	elemConvs := make([]conversion, len(tupleEtys))
 | 
			
		||||
	for i, tupleEty := range tupleEtys {
 | 
			
		||||
		if tupleEty.Equals(listEty) {
 | 
			
		||||
			// no conversion required
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		elemConvs[i] = getConversion(tupleEty, listEty, unsafe)
 | 
			
		||||
		if elemConvs[i] == nil {
 | 
			
		||||
			// If any of our element conversions are impossible, then the our
 | 
			
		||||
			// whole conversion is impossible.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we fall out here then a conversion is possible, using the
 | 
			
		||||
	// element conversions in elemConvs
 | 
			
		||||
	return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		elems := make([]cty.Value, 0, len(elemConvs))
 | 
			
		||||
		path = append(path, nil)
 | 
			
		||||
		i := int64(0)
 | 
			
		||||
		it := val.ElementIterator()
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			_, val := it.Element()
 | 
			
		||||
			var err error
 | 
			
		||||
 | 
			
		||||
			path[len(path)-1] = cty.IndexStep{
 | 
			
		||||
				Key: cty.NumberIntVal(i),
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			conv := elemConvs[i]
 | 
			
		||||
			if conv != nil {
 | 
			
		||||
				val, err = conv(val, path)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return cty.NilVal, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			elems = append(elems, val)
 | 
			
		||||
 | 
			
		||||
			i++
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return cty.ListVal(elems), nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// conversionObjectToMap returns a conversion that will take a value of the
 | 
			
		||||
// given object type and return a map of the given element type.
 | 
			
		||||
//
 | 
			
		||||
// Will panic if the given objectType isn't actually an object type.
 | 
			
		||||
func conversionObjectToMap(objectType cty.Type, mapEty cty.Type, unsafe bool) conversion {
 | 
			
		||||
	objectAtys := objectType.AttributeTypes()
 | 
			
		||||
 | 
			
		||||
	if len(objectAtys) == 0 {
 | 
			
		||||
		// Empty object short-circuit
 | 
			
		||||
		return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
			return cty.MapValEmpty(mapEty), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if mapEty == cty.DynamicPseudoType {
 | 
			
		||||
		// This is a special case where the caller wants us to find
 | 
			
		||||
		// a suitable single type that all elements can convert to, if
 | 
			
		||||
		// possible.
 | 
			
		||||
		objectAtysList := make([]cty.Type, 0, len(objectAtys))
 | 
			
		||||
		for _, aty := range objectAtys {
 | 
			
		||||
			objectAtysList = append(objectAtysList, aty)
 | 
			
		||||
		}
 | 
			
		||||
		mapEty, _ = unify(objectAtysList, unsafe)
 | 
			
		||||
		if mapEty == cty.NilType {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	elemConvs := make(map[string]conversion, len(objectAtys))
 | 
			
		||||
	for name, objectAty := range objectAtys {
 | 
			
		||||
		if objectAty.Equals(mapEty) {
 | 
			
		||||
			// no conversion required
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		elemConvs[name] = getConversion(objectAty, mapEty, unsafe)
 | 
			
		||||
		if elemConvs[name] == nil {
 | 
			
		||||
			// If any of our element conversions are impossible, then the our
 | 
			
		||||
			// whole conversion is impossible.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we fall out here then a conversion is possible, using the
 | 
			
		||||
	// element conversions in elemConvs
 | 
			
		||||
	return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		elems := make(map[string]cty.Value, len(elemConvs))
 | 
			
		||||
		path = append(path, nil)
 | 
			
		||||
		it := val.ElementIterator()
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			name, val := it.Element()
 | 
			
		||||
			var err error
 | 
			
		||||
 | 
			
		||||
			path[len(path)-1] = cty.IndexStep{
 | 
			
		||||
				Key: name,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			conv := elemConvs[name.AsString()]
 | 
			
		||||
			if conv != nil {
 | 
			
		||||
				val, err = conv(val, path)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return cty.NilVal, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			elems[name.AsString()] = val
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return cty.MapVal(elems), nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_dynamic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_dynamic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// dynamicFixup deals with just-in-time conversions of values that were
 | 
			
		||||
// input-typed as cty.DynamicPseudoType during analysis, ensuring that
 | 
			
		||||
// we end up with the desired output type once the value is known, or
 | 
			
		||||
// failing with an error if that is not possible.
 | 
			
		||||
//
 | 
			
		||||
// This is in the spirit of the cty philosophy of optimistically assuming that
 | 
			
		||||
// DynamicPseudoType values will become the intended value eventually, and
 | 
			
		||||
// dealing with any inconsistencies during final evaluation.
 | 
			
		||||
func dynamicFixup(wantType cty.Type) conversion {
 | 
			
		||||
	return func(in cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		ret, err := Convert(in, wantType)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// Re-wrap this error so that the returned path is relative
 | 
			
		||||
			// to the caller's original value, rather than relative to our
 | 
			
		||||
			// conversion value here.
 | 
			
		||||
			return cty.NilVal, path.NewError(err)
 | 
			
		||||
		}
 | 
			
		||||
		return ret, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dynamicPassthrough is an identity conversion that is used when the
 | 
			
		||||
// target type is DynamicPseudoType, indicating that the caller doesn't care
 | 
			
		||||
// which type is returned.
 | 
			
		||||
func dynamicPassthrough(in cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
	return in, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										76
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_object.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_object.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// conversionObjectToObject returns a conversion that will make the input
 | 
			
		||||
// object type conform to the output object type, if possible.
 | 
			
		||||
//
 | 
			
		||||
// Conversion is possible only if the output type is a subset of the input
 | 
			
		||||
// type, meaning that each attribute of the output type has a corresponding
 | 
			
		||||
// attribute in the input type where a recursive conversion is available.
 | 
			
		||||
//
 | 
			
		||||
// Shallow object conversions work the same for both safe and unsafe modes,
 | 
			
		||||
// but the safety flag is passed on to recursive conversions and may thus
 | 
			
		||||
// limit the above definition of "subset".
 | 
			
		||||
func conversionObjectToObject(in, out cty.Type, unsafe bool) conversion {
 | 
			
		||||
	inAtys := in.AttributeTypes()
 | 
			
		||||
	outAtys := out.AttributeTypes()
 | 
			
		||||
	attrConvs := make(map[string]conversion)
 | 
			
		||||
 | 
			
		||||
	for name, outAty := range outAtys {
 | 
			
		||||
		inAty, exists := inAtys[name]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			// No conversion is available, then.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if inAty.Equals(outAty) {
 | 
			
		||||
			// No conversion needed, but we'll still record the attribute
 | 
			
		||||
			// in our map for later reference.
 | 
			
		||||
			attrConvs[name] = nil
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		attrConvs[name] = getConversion(inAty, outAty, unsafe)
 | 
			
		||||
		if attrConvs[name] == nil {
 | 
			
		||||
			// If a recursive conversion isn't available, then our top-level
 | 
			
		||||
			// configuration is impossible too.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we get here then a conversion is possible, using the attribute
 | 
			
		||||
	// conversions given in attrConvs.
 | 
			
		||||
	return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		attrVals := make(map[string]cty.Value, len(attrConvs))
 | 
			
		||||
		path = append(path, nil)
 | 
			
		||||
		pathStep := &path[len(path)-1]
 | 
			
		||||
 | 
			
		||||
		for it := val.ElementIterator(); it.Next(); {
 | 
			
		||||
			nameVal, val := it.Element()
 | 
			
		||||
			var err error
 | 
			
		||||
 | 
			
		||||
			name := nameVal.AsString()
 | 
			
		||||
			*pathStep = cty.GetAttrStep{
 | 
			
		||||
				Name: name,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			conv, exists := attrConvs[name]
 | 
			
		||||
			if !exists {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if conv != nil {
 | 
			
		||||
				val, err = conv(val, path)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return cty.NilVal, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			attrVals[name] = val
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return cty.ObjectVal(attrVals), nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var stringTrue = cty.StringVal("true")
 | 
			
		||||
var stringFalse = cty.StringVal("false")
 | 
			
		||||
 | 
			
		||||
var primitiveConversionsSafe = map[cty.Type]map[cty.Type]conversion{
 | 
			
		||||
	cty.Number: {
 | 
			
		||||
		cty.String: func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
			f := val.AsBigFloat()
 | 
			
		||||
			return cty.StringVal(f.Text('f', -1)), nil
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	cty.Bool: {
 | 
			
		||||
		cty.String: func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
			if val.True() {
 | 
			
		||||
				return stringTrue, nil
 | 
			
		||||
			} else {
 | 
			
		||||
				return stringFalse, nil
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var primitiveConversionsUnsafe = map[cty.Type]map[cty.Type]conversion{
 | 
			
		||||
	cty.String: {
 | 
			
		||||
		cty.Number: func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
			v, err := cty.ParseNumberVal(val.AsString())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return cty.NilVal, path.NewErrorf("a number is required")
 | 
			
		||||
			}
 | 
			
		||||
			return v, nil
 | 
			
		||||
		},
 | 
			
		||||
		cty.Bool: func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
			switch val.AsString() {
 | 
			
		||||
			case "true", "1":
 | 
			
		||||
				return cty.True, nil
 | 
			
		||||
			case "false", "0":
 | 
			
		||||
				return cty.False, nil
 | 
			
		||||
			default:
 | 
			
		||||
				switch strings.ToLower(val.AsString()) {
 | 
			
		||||
				case "true":
 | 
			
		||||
					return cty.NilVal, path.NewErrorf("a bool is required; to convert from string, use lowercase \"true\"")
 | 
			
		||||
				case "false":
 | 
			
		||||
					return cty.NilVal, path.NewErrorf("a bool is required; to convert from string, use lowercase \"false\"")
 | 
			
		||||
				default:
 | 
			
		||||
					return cty.NilVal, path.NewErrorf("a bool is required")
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_tuple.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/conversion_tuple.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// conversionTupleToTuple returns a conversion that will make the input
 | 
			
		||||
// tuple type conform to the output tuple type, if possible.
 | 
			
		||||
//
 | 
			
		||||
// Conversion is possible only if the two tuple types have the same number
 | 
			
		||||
// of elements and the corresponding elements by index can be converted.
 | 
			
		||||
//
 | 
			
		||||
// Shallow tuple conversions work the same for both safe and unsafe modes,
 | 
			
		||||
// but the safety flag is passed on to recursive conversions and may thus
 | 
			
		||||
// limit which element type conversions are possible.
 | 
			
		||||
func conversionTupleToTuple(in, out cty.Type, unsafe bool) conversion {
 | 
			
		||||
	inEtys := in.TupleElementTypes()
 | 
			
		||||
	outEtys := out.TupleElementTypes()
 | 
			
		||||
 | 
			
		||||
	if len(inEtys) != len(outEtys) {
 | 
			
		||||
		return nil // no conversion is possible
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	elemConvs := make([]conversion, len(inEtys))
 | 
			
		||||
 | 
			
		||||
	for i, outEty := range outEtys {
 | 
			
		||||
		inEty := inEtys[i]
 | 
			
		||||
 | 
			
		||||
		if inEty.Equals(outEty) {
 | 
			
		||||
			// No conversion needed, so we can leave this one nil.
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		elemConvs[i] = getConversion(inEty, outEty, unsafe)
 | 
			
		||||
		if elemConvs[i] == nil {
 | 
			
		||||
			// If a recursive conversion isn't available, then our top-level
 | 
			
		||||
			// configuration is impossible too.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we get here then a conversion is possible, using the element
 | 
			
		||||
	// conversions given in elemConvs.
 | 
			
		||||
	return func(val cty.Value, path cty.Path) (cty.Value, error) {
 | 
			
		||||
		elemVals := make([]cty.Value, len(elemConvs))
 | 
			
		||||
		path = append(path, nil)
 | 
			
		||||
		pathStep := &path[len(path)-1]
 | 
			
		||||
 | 
			
		||||
		i := 0
 | 
			
		||||
		for it := val.ElementIterator(); it.Next(); i++ {
 | 
			
		||||
			_, val := it.Element()
 | 
			
		||||
			var err error
 | 
			
		||||
 | 
			
		||||
			*pathStep = cty.IndexStep{
 | 
			
		||||
				Key: cty.NumberIntVal(int64(i)),
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			conv := elemConvs[i]
 | 
			
		||||
			if conv != nil {
 | 
			
		||||
				val, err = conv(val, path)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return cty.NilVal, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			elemVals[i] = val
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return cty.TupleVal(elemVals), nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
// Package convert contains some routines for converting between cty types.
 | 
			
		||||
// The intent of providing this package is to encourage applications using
 | 
			
		||||
// cty to have consistent type conversion behavior for maximal interoperability
 | 
			
		||||
// when Values pass from one application to another.
 | 
			
		||||
//
 | 
			
		||||
// The conversions are categorized into two categories. "Safe" conversions are
 | 
			
		||||
// ones that are guaranteed to succeed if given a non-null value of the
 | 
			
		||||
// appropriate source type. "Unsafe" conversions, on the other hand, are valid
 | 
			
		||||
// for only a subset of input values, and thus may fail with an error when
 | 
			
		||||
// called for values outside of that valid subset.
 | 
			
		||||
//
 | 
			
		||||
// The functions whose names end in Unsafe support all of the conversions that
 | 
			
		||||
// are supported by the corresponding functions whose names do not have that
 | 
			
		||||
// suffix, and then additional unsafe conversions as well.
 | 
			
		||||
package convert
 | 
			
		||||
							
								
								
									
										220
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/mismatch_msg.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/mismatch_msg.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,220 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MismatchMessage is a helper to return an English-language description of
 | 
			
		||||
// the differences between got and want, phrased as a reason why got does
 | 
			
		||||
// not conform to want.
 | 
			
		||||
//
 | 
			
		||||
// This function does not itself attempt conversion, and so it should generally
 | 
			
		||||
// be used only after a conversion has failed, to report the conversion failure
 | 
			
		||||
// to an English-speaking user. The result will be confusing got is actually
 | 
			
		||||
// conforming to or convertable to want.
 | 
			
		||||
//
 | 
			
		||||
// The shorthand helper function Convert uses this function internally to
 | 
			
		||||
// produce its error messages, so callers of that function do not need to
 | 
			
		||||
// also use MismatchMessage.
 | 
			
		||||
//
 | 
			
		||||
// This function is similar to Type.TestConformance, but it is tailored to
 | 
			
		||||
// describing conversion failures and so the messages it generates relate
 | 
			
		||||
// specifically to the conversion rules implemented in this package.
 | 
			
		||||
func MismatchMessage(got, want cty.Type) string {
 | 
			
		||||
	switch {
 | 
			
		||||
 | 
			
		||||
	case got.IsObjectType() && want.IsObjectType():
 | 
			
		||||
		// If both types are object types then we may be able to say something
 | 
			
		||||
		// about their respective attributes.
 | 
			
		||||
		return mismatchMessageObjects(got, want)
 | 
			
		||||
 | 
			
		||||
	case got.IsTupleType() && want.IsListType() && want.ElementType() == cty.DynamicPseudoType:
 | 
			
		||||
		// If conversion from tuple to list failed then it's because we couldn't
 | 
			
		||||
		// find a common type to convert all of the tuple elements to.
 | 
			
		||||
		return "all list elements must have the same type"
 | 
			
		||||
 | 
			
		||||
	case got.IsTupleType() && want.IsSetType() && want.ElementType() == cty.DynamicPseudoType:
 | 
			
		||||
		// If conversion from tuple to set failed then it's because we couldn't
 | 
			
		||||
		// find a common type to convert all of the tuple elements to.
 | 
			
		||||
		return "all set elements must have the same type"
 | 
			
		||||
 | 
			
		||||
	case got.IsObjectType() && want.IsMapType() && want.ElementType() == cty.DynamicPseudoType:
 | 
			
		||||
		// If conversion from object to map failed then it's because we couldn't
 | 
			
		||||
		// find a common type to convert all of the object attributes to.
 | 
			
		||||
		return "all map elements must have the same type"
 | 
			
		||||
 | 
			
		||||
	case (got.IsTupleType() || got.IsObjectType()) && want.IsCollectionType():
 | 
			
		||||
		return mismatchMessageCollectionsFromStructural(got, want)
 | 
			
		||||
 | 
			
		||||
	case got.IsCollectionType() && want.IsCollectionType():
 | 
			
		||||
		return mismatchMessageCollectionsFromCollections(got, want)
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		// If we have nothing better to say, we'll just state what was required.
 | 
			
		||||
		return want.FriendlyNameForConstraint() + " required"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mismatchMessageObjects(got, want cty.Type) string {
 | 
			
		||||
	// Per our conversion rules, "got" is allowed to be a superset of "want",
 | 
			
		||||
	// and so we'll produce error messages here under that assumption.
 | 
			
		||||
	gotAtys := got.AttributeTypes()
 | 
			
		||||
	wantAtys := want.AttributeTypes()
 | 
			
		||||
 | 
			
		||||
	// If we find missing attributes then we'll report those in preference,
 | 
			
		||||
	// but if not then we will report a maximum of one non-conforming
 | 
			
		||||
	// attribute, just to keep our messages relatively terse.
 | 
			
		||||
	// We'll also prefer to report a recursive type error from an _unsafe_
 | 
			
		||||
	// conversion over a safe one, because these are subjectively more
 | 
			
		||||
	// "serious".
 | 
			
		||||
	var missingAttrs []string
 | 
			
		||||
	var unsafeMismatchAttr string
 | 
			
		||||
	var safeMismatchAttr string
 | 
			
		||||
 | 
			
		||||
	for name, wantAty := range wantAtys {
 | 
			
		||||
		gotAty, exists := gotAtys[name]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			missingAttrs = append(missingAttrs, name)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// We'll now try to convert these attributes in isolation and
 | 
			
		||||
		// see if we have a nested conversion error to report.
 | 
			
		||||
		// We'll try an unsafe conversion first, and then fall back on
 | 
			
		||||
		// safe if unsafe is possible.
 | 
			
		||||
 | 
			
		||||
		// If we already have an unsafe mismatch attr error then we won't bother
 | 
			
		||||
		// hunting for another one.
 | 
			
		||||
		if unsafeMismatchAttr != "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if conv := GetConversionUnsafe(gotAty, wantAty); conv == nil {
 | 
			
		||||
			unsafeMismatchAttr = fmt.Sprintf("attribute %q: %s", name, MismatchMessage(gotAty, wantAty))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If we already have a safe mismatch attr error then we won't bother
 | 
			
		||||
		// hunting for another one.
 | 
			
		||||
		if safeMismatchAttr != "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if conv := GetConversion(gotAty, wantAty); conv == nil {
 | 
			
		||||
			safeMismatchAttr = fmt.Sprintf("attribute %q: %s", name, MismatchMessage(gotAty, wantAty))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We should now have collected at least one problem. If we have more than
 | 
			
		||||
	// one then we'll use our preference order to decide what is most important
 | 
			
		||||
	// to report.
 | 
			
		||||
	switch {
 | 
			
		||||
 | 
			
		||||
	case len(missingAttrs) != 0:
 | 
			
		||||
		sort.Strings(missingAttrs)
 | 
			
		||||
		switch len(missingAttrs) {
 | 
			
		||||
		case 1:
 | 
			
		||||
			return fmt.Sprintf("attribute %q is required", missingAttrs[0])
 | 
			
		||||
		case 2:
 | 
			
		||||
			return fmt.Sprintf("attributes %q and %q are required", missingAttrs[0], missingAttrs[1])
 | 
			
		||||
		default:
 | 
			
		||||
			sort.Strings(missingAttrs)
 | 
			
		||||
			var buf bytes.Buffer
 | 
			
		||||
			for _, name := range missingAttrs[:len(missingAttrs)-1] {
 | 
			
		||||
				fmt.Fprintf(&buf, "%q, ", name)
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Fprintf(&buf, "and %q", missingAttrs[len(missingAttrs)-1])
 | 
			
		||||
			return fmt.Sprintf("attributes %s are required", buf.Bytes())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case unsafeMismatchAttr != "":
 | 
			
		||||
		return unsafeMismatchAttr
 | 
			
		||||
 | 
			
		||||
	case safeMismatchAttr != "":
 | 
			
		||||
		return safeMismatchAttr
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		// We should never get here, but if we do then we'll return
 | 
			
		||||
		// just a generic message.
 | 
			
		||||
		return "incorrect object attributes"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mismatchMessageCollectionsFromStructural(got, want cty.Type) string {
 | 
			
		||||
	// First some straightforward cases where the kind is just altogether wrong.
 | 
			
		||||
	switch {
 | 
			
		||||
	case want.IsListType() && !got.IsTupleType():
 | 
			
		||||
		return want.FriendlyNameForConstraint() + " required"
 | 
			
		||||
	case want.IsSetType() && !got.IsTupleType():
 | 
			
		||||
		return want.FriendlyNameForConstraint() + " required"
 | 
			
		||||
	case want.IsMapType() && !got.IsObjectType():
 | 
			
		||||
		return want.FriendlyNameForConstraint() + " required"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If the kinds are matched well enough then we'll move on to checking
 | 
			
		||||
	// individual elements.
 | 
			
		||||
	wantEty := want.ElementType()
 | 
			
		||||
	switch {
 | 
			
		||||
	case got.IsTupleType():
 | 
			
		||||
		for i, gotEty := range got.TupleElementTypes() {
 | 
			
		||||
			if gotEty.Equals(wantEty) {
 | 
			
		||||
				continue // exact match, so no problem
 | 
			
		||||
			}
 | 
			
		||||
			if conv := getConversion(gotEty, wantEty, true); conv != nil {
 | 
			
		||||
				continue // conversion is available, so no problem
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Sprintf("element %d: %s", i, MismatchMessage(gotEty, wantEty))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If we get down here then something weird is going on but we'll
 | 
			
		||||
		// return a reasonable fallback message anyway.
 | 
			
		||||
		return fmt.Sprintf("all elements must be %s", wantEty.FriendlyNameForConstraint())
 | 
			
		||||
 | 
			
		||||
	case got.IsObjectType():
 | 
			
		||||
		for name, gotAty := range got.AttributeTypes() {
 | 
			
		||||
			if gotAty.Equals(wantEty) {
 | 
			
		||||
				continue // exact match, so no problem
 | 
			
		||||
			}
 | 
			
		||||
			if conv := getConversion(gotAty, wantEty, true); conv != nil {
 | 
			
		||||
				continue // conversion is available, so no problem
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Sprintf("element %q: %s", name, MismatchMessage(gotAty, wantEty))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If we get down here then something weird is going on but we'll
 | 
			
		||||
		// return a reasonable fallback message anyway.
 | 
			
		||||
		return fmt.Sprintf("all elements must be %s", wantEty.FriendlyNameForConstraint())
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		// Should not be possible to get here since we only call this function
 | 
			
		||||
		// with got as structural types, but...
 | 
			
		||||
		return want.FriendlyNameForConstraint() + " required"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mismatchMessageCollectionsFromCollections(got, want cty.Type) string {
 | 
			
		||||
	// First some straightforward cases where the kind is just altogether wrong.
 | 
			
		||||
	switch {
 | 
			
		||||
	case want.IsListType() && !(got.IsListType() || got.IsSetType()):
 | 
			
		||||
		return want.FriendlyNameForConstraint() + " required"
 | 
			
		||||
	case want.IsSetType() && !(got.IsListType() || got.IsSetType()):
 | 
			
		||||
		return want.FriendlyNameForConstraint() + " required"
 | 
			
		||||
	case want.IsMapType() && !got.IsMapType():
 | 
			
		||||
		return want.FriendlyNameForConstraint() + " required"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If the kinds are matched well enough then we'll check the element types.
 | 
			
		||||
	gotEty := got.ElementType()
 | 
			
		||||
	wantEty := want.ElementType()
 | 
			
		||||
	noun := "element type"
 | 
			
		||||
	switch {
 | 
			
		||||
	case want.IsListType():
 | 
			
		||||
		noun = "list element type"
 | 
			
		||||
	case want.IsSetType():
 | 
			
		||||
		noun = "set element type"
 | 
			
		||||
	case want.IsMapType():
 | 
			
		||||
		noun = "map element type"
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("incorrect %s: %s", noun, MismatchMessage(gotEty, wantEty))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										83
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/public.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/public.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This file contains the public interface of this package, which is intended
 | 
			
		||||
// to be a small, convenient interface designed for easy integration into
 | 
			
		||||
// a hypothetical language type checker and interpreter.
 | 
			
		||||
 | 
			
		||||
// Conversion is a named function type representing a conversion from a
 | 
			
		||||
// value of one type to a value of another type.
 | 
			
		||||
//
 | 
			
		||||
// The source type for a conversion is always the source type given to
 | 
			
		||||
// the function that returned the Conversion, but there is no way to recover
 | 
			
		||||
// that from a Conversion value itself. If a Conversion is given a value
 | 
			
		||||
// that is not of its expected type (with the exception of DynamicPseudoType,
 | 
			
		||||
// which is always supported) then the function may panic or produce undefined
 | 
			
		||||
// results.
 | 
			
		||||
type Conversion func(in cty.Value) (out cty.Value, err error)
 | 
			
		||||
 | 
			
		||||
// GetConversion returns a Conversion between the given in and out Types if
 | 
			
		||||
// a safe one is available, or returns nil otherwise.
 | 
			
		||||
func GetConversion(in cty.Type, out cty.Type) Conversion {
 | 
			
		||||
	return retConversion(getConversion(in, out, false))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetConversionUnsafe returns a Conversion between the given in and out Types
 | 
			
		||||
// if either a safe or unsafe one is available, or returns nil otherwise.
 | 
			
		||||
func GetConversionUnsafe(in cty.Type, out cty.Type) Conversion {
 | 
			
		||||
	return retConversion(getConversion(in, out, true))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convert returns the result of converting the given value to the given type
 | 
			
		||||
// if an safe or unsafe conversion is available, or returns an error if such a
 | 
			
		||||
// conversion is impossible.
 | 
			
		||||
//
 | 
			
		||||
// This is a convenience wrapper around calling GetConversionUnsafe and then
 | 
			
		||||
// immediately passing the given value to the resulting function.
 | 
			
		||||
func Convert(in cty.Value, want cty.Type) (cty.Value, error) {
 | 
			
		||||
	if in.Type().Equals(want) {
 | 
			
		||||
		return in, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conv := GetConversionUnsafe(in.Type(), want)
 | 
			
		||||
	if conv == nil {
 | 
			
		||||
		return cty.NilVal, errors.New(MismatchMessage(in.Type(), want))
 | 
			
		||||
	}
 | 
			
		||||
	return conv(in)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unify attempts to find the most general type that can be converted from
 | 
			
		||||
// all of the given types. If this is possible, that type is returned along
 | 
			
		||||
// with a slice of necessary conversions for some of the given types.
 | 
			
		||||
//
 | 
			
		||||
// If no common supertype can be found, this function returns cty.NilType and
 | 
			
		||||
// a nil slice.
 | 
			
		||||
//
 | 
			
		||||
// If a common supertype *can* be found, the returned slice will always be
 | 
			
		||||
// non-nil and will contain a non-nil conversion for each given type that
 | 
			
		||||
// needs to be converted, with indices corresponding to the input slice.
 | 
			
		||||
// Any given type that does *not* need conversion (because it is already of
 | 
			
		||||
// the appropriate type) will have a nil Conversion.
 | 
			
		||||
//
 | 
			
		||||
// cty.DynamicPseudoType is, as usual, a special case. If the given type list
 | 
			
		||||
// contains a mixture of dynamic and non-dynamic types, the dynamic types are
 | 
			
		||||
// disregarded for type selection and a conversion is returned for them that
 | 
			
		||||
// will attempt a late conversion of the given value to the target type,
 | 
			
		||||
// failing with a conversion error if the eventual concrete type is not
 | 
			
		||||
// compatible. If *all* given types are DynamicPseudoType, or in the
 | 
			
		||||
// degenerate case of an empty slice of types, the returned type is itself
 | 
			
		||||
// cty.DynamicPseudoType and no conversions are attempted.
 | 
			
		||||
func Unify(types []cty.Type) (cty.Type, []Conversion) {
 | 
			
		||||
	return unify(types, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnifyUnsafe is the same as Unify except that it may return unsafe
 | 
			
		||||
// conversions in situations where a safe conversion isn't also available.
 | 
			
		||||
func UnifyUnsafe(types []cty.Type) (cty.Type, []Conversion) {
 | 
			
		||||
	return unify(types, true)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/sort_types.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/sort_types.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// sortTypes produces an ordering of the given types that serves as a
 | 
			
		||||
// preference order for the result of unification of the given types.
 | 
			
		||||
// The return value is a slice of indices into the given slice, and will
 | 
			
		||||
// thus always be the same length as the given slice.
 | 
			
		||||
//
 | 
			
		||||
// The goal is that the most general of the given types will appear first
 | 
			
		||||
// in the ordering. If there are uncomparable pairs of types in the list
 | 
			
		||||
// then they will appear in an undefined order, and the unification pass
 | 
			
		||||
// will presumably then fail.
 | 
			
		||||
func sortTypes(tys []cty.Type) []int {
 | 
			
		||||
	l := len(tys)
 | 
			
		||||
 | 
			
		||||
	// First we build a graph whose edges represent "more general than",
 | 
			
		||||
	// which we will then do a topological sort of.
 | 
			
		||||
	edges := make([][]int, l)
 | 
			
		||||
	for i := 0; i < (l - 1); i++ {
 | 
			
		||||
		for j := i + 1; j < l; j++ {
 | 
			
		||||
			cmp := compareTypes(tys[i], tys[j])
 | 
			
		||||
			switch {
 | 
			
		||||
			case cmp < 0:
 | 
			
		||||
				edges[i] = append(edges[i], j)
 | 
			
		||||
			case cmp > 0:
 | 
			
		||||
				edges[j] = append(edges[j], i)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Compute the in-degree of each node
 | 
			
		||||
	inDegree := make([]int, l)
 | 
			
		||||
	for _, outs := range edges {
 | 
			
		||||
		for _, j := range outs {
 | 
			
		||||
			inDegree[j]++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The array backing our result will double as our queue for visiting
 | 
			
		||||
	// the nodes, with the queue slice moving along this array until it
 | 
			
		||||
	// is empty and positioned at the end of the array. Thus our visiting
 | 
			
		||||
	// order is also our result order.
 | 
			
		||||
	result := make([]int, l)
 | 
			
		||||
	queue := result[0:0]
 | 
			
		||||
 | 
			
		||||
	// Initialize the queue with any item of in-degree 0, preserving
 | 
			
		||||
	// their relative order.
 | 
			
		||||
	for i, n := range inDegree {
 | 
			
		||||
		if n == 0 {
 | 
			
		||||
			queue = append(queue, i)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for len(queue) != 0 {
 | 
			
		||||
		i := queue[0]
 | 
			
		||||
		queue = queue[1:]
 | 
			
		||||
		for _, j := range edges[i] {
 | 
			
		||||
			inDegree[j]--
 | 
			
		||||
			if inDegree[j] == 0 {
 | 
			
		||||
				queue = append(queue, j)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										314
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/unify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								vendor/github.com/zclconf/go-cty/cty/convert/unify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,314 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/zclconf/go-cty/cty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// The current unify implementation is somewhat inefficient, but we accept this
 | 
			
		||||
// under the assumption that it will generally be used with small numbers of
 | 
			
		||||
// types and with types of reasonable complexity. However, it does have a
 | 
			
		||||
// "happy path" where all of the given types are equal.
 | 
			
		||||
//
 | 
			
		||||
// This function is likely to have poor performance in cases where any given
 | 
			
		||||
// types are very complex (lots of deeply-nested structures) or if the list
 | 
			
		||||
// of types itself is very large. In particular, it will walk the nested type
 | 
			
		||||
// structure under the given types several times, especially when given a
 | 
			
		||||
// list of types for which unification is not possible, since each permutation
 | 
			
		||||
// will be tried to determine that result.
 | 
			
		||||
func unify(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
 | 
			
		||||
	if len(types) == 0 {
 | 
			
		||||
		// Degenerate case
 | 
			
		||||
		return cty.NilType, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If all of the given types are of the same structural kind, we may be
 | 
			
		||||
	// able to construct a new type that they can all be unified to, even if
 | 
			
		||||
	// that is not one of the given types. We must try this before the general
 | 
			
		||||
	// behavior below because in unsafe mode we can convert an object type to
 | 
			
		||||
	// a subset of that type, which would be a much less useful conversion for
 | 
			
		||||
	// unification purposes.
 | 
			
		||||
	{
 | 
			
		||||
		objectCt := 0
 | 
			
		||||
		tupleCt := 0
 | 
			
		||||
		dynamicCt := 0
 | 
			
		||||
		for _, ty := range types {
 | 
			
		||||
			switch {
 | 
			
		||||
			case ty.IsObjectType():
 | 
			
		||||
				objectCt++
 | 
			
		||||
			case ty.IsTupleType():
 | 
			
		||||
				tupleCt++
 | 
			
		||||
			case ty == cty.DynamicPseudoType:
 | 
			
		||||
				dynamicCt++
 | 
			
		||||
			default:
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		switch {
 | 
			
		||||
		case objectCt > 0 && (objectCt+dynamicCt) == len(types):
 | 
			
		||||
			return unifyObjectTypes(types, unsafe, dynamicCt > 0)
 | 
			
		||||
		case tupleCt > 0 && (tupleCt+dynamicCt) == len(types):
 | 
			
		||||
			return unifyTupleTypes(types, unsafe, dynamicCt > 0)
 | 
			
		||||
		case objectCt > 0 && tupleCt > 0:
 | 
			
		||||
			// Can never unify object and tuple types since they have incompatible kinds
 | 
			
		||||
			return cty.NilType, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	prefOrder := sortTypes(types)
 | 
			
		||||
 | 
			
		||||
	// sortTypes gives us an order where earlier items are preferable as
 | 
			
		||||
	// our result type. We'll now walk through these and choose the first
 | 
			
		||||
	// one we encounter for which conversions exist for all source types.
 | 
			
		||||
	conversions := make([]Conversion, len(types))
 | 
			
		||||
Preferences:
 | 
			
		||||
	for _, wantTypeIdx := range prefOrder {
 | 
			
		||||
		wantType := types[wantTypeIdx]
 | 
			
		||||
		for i, tryType := range types {
 | 
			
		||||
			if i == wantTypeIdx {
 | 
			
		||||
				// Don't need to convert our wanted type to itself
 | 
			
		||||
				conversions[i] = nil
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if tryType.Equals(wantType) {
 | 
			
		||||
				conversions[i] = nil
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if unsafe {
 | 
			
		||||
				conversions[i] = GetConversionUnsafe(tryType, wantType)
 | 
			
		||||
			} else {
 | 
			
		||||
				conversions[i] = GetConversion(tryType, wantType)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if conversions[i] == nil {
 | 
			
		||||
				// wantType is not a suitable unification type, so we'll
 | 
			
		||||
				// try the next one in our preference order.
 | 
			
		||||
				continue Preferences
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return wantType, conversions
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we fall out here, no unification is possible
 | 
			
		||||
	return cty.NilType, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unifyObjectTypes(types []cty.Type, unsafe bool, hasDynamic bool) (cty.Type, []Conversion) {
 | 
			
		||||
	// If we had any dynamic types in the input here then we can't predict
 | 
			
		||||
	// what path we'll take through here once these become known types, so
 | 
			
		||||
	// we'll conservatively produce DynamicVal for these.
 | 
			
		||||
	if hasDynamic {
 | 
			
		||||
		return unifyAllAsDynamic(types)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// There are two different ways we can succeed here:
 | 
			
		||||
	// - If all of the given object types have the same set of attribute names
 | 
			
		||||
	//   and the corresponding types are all unifyable, then we construct that
 | 
			
		||||
	//   type.
 | 
			
		||||
	// - If the given object types have different attribute names or their
 | 
			
		||||
	//   corresponding types are not unifyable, we'll instead try to unify
 | 
			
		||||
	//   all of the attribute types together to produce a map type.
 | 
			
		||||
	//
 | 
			
		||||
	// Our unification behavior is intentionally stricter than our conversion
 | 
			
		||||
	// behavior for subset object types because user intent is different with
 | 
			
		||||
	// unification use-cases: it makes sense to allow {"foo":true} to convert
 | 
			
		||||
	// to emptyobjectval, but unifying an object with an attribute with the
 | 
			
		||||
	// empty object type should be an error because unifying to the empty
 | 
			
		||||
	// object type would be suprising and useless.
 | 
			
		||||
 | 
			
		||||
	firstAttrs := types[0].AttributeTypes()
 | 
			
		||||
	for _, ty := range types[1:] {
 | 
			
		||||
		thisAttrs := ty.AttributeTypes()
 | 
			
		||||
		if len(thisAttrs) != len(firstAttrs) {
 | 
			
		||||
			// If number of attributes is different then there can be no
 | 
			
		||||
			// object type in common.
 | 
			
		||||
			return unifyObjectTypesToMap(types, unsafe)
 | 
			
		||||
		}
 | 
			
		||||
		for name := range thisAttrs {
 | 
			
		||||
			if _, ok := firstAttrs[name]; !ok {
 | 
			
		||||
				// If attribute names don't exactly match then there can be
 | 
			
		||||
				// no object type in common.
 | 
			
		||||
				return unifyObjectTypesToMap(types, unsafe)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we get here then we've proven that all of the given object types
 | 
			
		||||
	// have exactly the same set of attribute names, though the types may
 | 
			
		||||
	// differ.
 | 
			
		||||
	retAtys := make(map[string]cty.Type)
 | 
			
		||||
	atysAcross := make([]cty.Type, len(types))
 | 
			
		||||
	for name := range firstAttrs {
 | 
			
		||||
		for i, ty := range types {
 | 
			
		||||
			atysAcross[i] = ty.AttributeType(name)
 | 
			
		||||
		}
 | 
			
		||||
		retAtys[name], _ = unify(atysAcross, unsafe)
 | 
			
		||||
		if retAtys[name] == cty.NilType {
 | 
			
		||||
			// Cannot unify this attribute alone, which means that unification
 | 
			
		||||
			// of everything down to a map type can't be possible either.
 | 
			
		||||
			return cty.NilType, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	retTy := cty.Object(retAtys)
 | 
			
		||||
 | 
			
		||||
	conversions := make([]Conversion, len(types))
 | 
			
		||||
	for i, ty := range types {
 | 
			
		||||
		if ty.Equals(retTy) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if unsafe {
 | 
			
		||||
			conversions[i] = GetConversionUnsafe(ty, retTy)
 | 
			
		||||
		} else {
 | 
			
		||||
			conversions[i] = GetConversion(ty, retTy)
 | 
			
		||||
		}
 | 
			
		||||
		if conversions[i] == nil {
 | 
			
		||||
			// Shouldn't be reachable, since we were able to unify
 | 
			
		||||
			return unifyObjectTypesToMap(types, unsafe)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return retTy, conversions
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unifyObjectTypesToMap(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
 | 
			
		||||
	// This is our fallback case for unifyObjectTypes, where we see if we can
 | 
			
		||||
	// construct a map type that can accept all of the attribute types.
 | 
			
		||||
 | 
			
		||||
	var atys []cty.Type
 | 
			
		||||
	for _, ty := range types {
 | 
			
		||||
		for _, aty := range ty.AttributeTypes() {
 | 
			
		||||
			atys = append(atys, aty)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ety, _ := unify(atys, unsafe)
 | 
			
		||||
	if ety == cty.NilType {
 | 
			
		||||
		return cty.NilType, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	retTy := cty.Map(ety)
 | 
			
		||||
	conversions := make([]Conversion, len(types))
 | 
			
		||||
	for i, ty := range types {
 | 
			
		||||
		if ty.Equals(retTy) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if unsafe {
 | 
			
		||||
			conversions[i] = GetConversionUnsafe(ty, retTy)
 | 
			
		||||
		} else {
 | 
			
		||||
			conversions[i] = GetConversion(ty, retTy)
 | 
			
		||||
		}
 | 
			
		||||
		if conversions[i] == nil {
 | 
			
		||||
			return cty.NilType, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return retTy, conversions
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unifyTupleTypes(types []cty.Type, unsafe bool, hasDynamic bool) (cty.Type, []Conversion) {
 | 
			
		||||
	// If we had any dynamic types in the input here then we can't predict
 | 
			
		||||
	// what path we'll take through here once these become known types, so
 | 
			
		||||
	// we'll conservatively produce DynamicVal for these.
 | 
			
		||||
	if hasDynamic {
 | 
			
		||||
		return unifyAllAsDynamic(types)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// There are two different ways we can succeed here:
 | 
			
		||||
	// - If all of the given tuple types have the same sequence of element types
 | 
			
		||||
	//   and the corresponding types are all unifyable, then we construct that
 | 
			
		||||
	//   type.
 | 
			
		||||
	// - If the given tuple types have different element types or their
 | 
			
		||||
	//   corresponding types are not unifyable, we'll instead try to unify
 | 
			
		||||
	//   all of the elements types together to produce a list type.
 | 
			
		||||
 | 
			
		||||
	firstEtys := types[0].TupleElementTypes()
 | 
			
		||||
	for _, ty := range types[1:] {
 | 
			
		||||
		thisEtys := ty.TupleElementTypes()
 | 
			
		||||
		if len(thisEtys) != len(firstEtys) {
 | 
			
		||||
			// If number of elements is different then there can be no
 | 
			
		||||
			// tuple type in common.
 | 
			
		||||
			return unifyTupleTypesToList(types, unsafe)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we get here then we've proven that all of the given tuple types
 | 
			
		||||
	// have the same number of elements, though the types may differ.
 | 
			
		||||
	retEtys := make([]cty.Type, len(firstEtys))
 | 
			
		||||
	atysAcross := make([]cty.Type, len(types))
 | 
			
		||||
	for idx := range firstEtys {
 | 
			
		||||
		for tyI, ty := range types {
 | 
			
		||||
			atysAcross[tyI] = ty.TupleElementTypes()[idx]
 | 
			
		||||
		}
 | 
			
		||||
		retEtys[idx], _ = unify(atysAcross, unsafe)
 | 
			
		||||
		if retEtys[idx] == cty.NilType {
 | 
			
		||||
			// Cannot unify this element alone, which means that unification
 | 
			
		||||
			// of everything down to a map type can't be possible either.
 | 
			
		||||
			return cty.NilType, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	retTy := cty.Tuple(retEtys)
 | 
			
		||||
 | 
			
		||||
	conversions := make([]Conversion, len(types))
 | 
			
		||||
	for i, ty := range types {
 | 
			
		||||
		if ty.Equals(retTy) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if unsafe {
 | 
			
		||||
			conversions[i] = GetConversionUnsafe(ty, retTy)
 | 
			
		||||
		} else {
 | 
			
		||||
			conversions[i] = GetConversion(ty, retTy)
 | 
			
		||||
		}
 | 
			
		||||
		if conversions[i] == nil {
 | 
			
		||||
			// Shouldn't be reachable, since we were able to unify
 | 
			
		||||
			return unifyTupleTypesToList(types, unsafe)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return retTy, conversions
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unifyTupleTypesToList(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
 | 
			
		||||
	// This is our fallback case for unifyTupleTypes, where we see if we can
 | 
			
		||||
	// construct a list type that can accept all of the element types.
 | 
			
		||||
 | 
			
		||||
	var etys []cty.Type
 | 
			
		||||
	for _, ty := range types {
 | 
			
		||||
		for _, ety := range ty.TupleElementTypes() {
 | 
			
		||||
			etys = append(etys, ety)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ety, _ := unify(etys, unsafe)
 | 
			
		||||
	if ety == cty.NilType {
 | 
			
		||||
		return cty.NilType, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	retTy := cty.List(ety)
 | 
			
		||||
	conversions := make([]Conversion, len(types))
 | 
			
		||||
	for i, ty := range types {
 | 
			
		||||
		if ty.Equals(retTy) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if unsafe {
 | 
			
		||||
			conversions[i] = GetConversionUnsafe(ty, retTy)
 | 
			
		||||
		} else {
 | 
			
		||||
			conversions[i] = GetConversion(ty, retTy)
 | 
			
		||||
		}
 | 
			
		||||
		if conversions[i] == nil {
 | 
			
		||||
			// Shouldn't be reachable, since we were able to unify
 | 
			
		||||
			return unifyObjectTypesToMap(types, unsafe)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return retTy, conversions
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unifyAllAsDynamic(types []cty.Type) (cty.Type, []Conversion) {
 | 
			
		||||
	conversions := make([]Conversion, len(types))
 | 
			
		||||
	for i := range conversions {
 | 
			
		||||
		conversions[i] = func(cty.Value) (cty.Value, error) {
 | 
			
		||||
			return cty.DynamicVal, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return cty.DynamicPseudoType, conversions
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user