Upgrade hcl to v2

Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
This commit is contained in:
Patrick Van Stee
2020-04-15 21:00:17 -04:00
parent 09339bf500
commit 87c4bf1df9
129 changed files with 24573 additions and 4047 deletions

128
vendor/github.com/zclconf/go-cty/cty/capsule.go generated vendored Normal file
View File

@ -0,0 +1,128 @@
package cty
import (
"fmt"
"reflect"
)
type capsuleType struct {
typeImplSigil
Name string
GoType reflect.Type
Ops *CapsuleOps
}
func (t *capsuleType) Equals(other Type) bool {
if otherP, ok := other.typeImpl.(*capsuleType); ok {
// capsule types compare by pointer identity
return otherP == t
}
return false
}
func (t *capsuleType) FriendlyName(mode friendlyTypeNameMode) string {
return t.Name
}
func (t *capsuleType) GoString() string {
impl := t.Ops.TypeGoString
if impl == nil {
// To get a useful representation of our native type requires some
// shenanigans.
victimVal := reflect.Zero(t.GoType)
if t.Ops == noCapsuleOps {
return fmt.Sprintf("cty.Capsule(%q, reflect.TypeOf(%#v))", t.Name, victimVal.Interface())
} else {
// Including the operations in the output will make this _very_ long,
// so in practice any capsule type with ops ought to provide a
// TypeGoString function to override this with something more
// reasonable.
return fmt.Sprintf("cty.CapsuleWithOps(%q, reflect.TypeOf(%#v), %#v)", t.Name, victimVal.Interface(), t.Ops)
}
}
return impl(t.GoType)
}
// Capsule creates a new Capsule type.
//
// A Capsule type is a special type that can be used to transport arbitrary
// Go native values of a given type through the cty type system. A language
// that uses cty as its type system might, for example, provide functions
// that return capsule-typed values and then other functions that operate
// on those values.
//
// From cty's perspective, Capsule types have a few interesting characteristics,
// described in the following paragraphs.
//
// Each capsule type has an associated Go native type that it is able to
// transport. Capsule types compare by identity, so each call to the
// Capsule function creates an entirely-distinct cty Type, even if two calls
// use the same native type.
//
// Each capsule-typed value contains a pointer to a value of the given native
// type. A capsule-typed value by default supports no operations except
// equality, and equality is implemented by pointer identity of the
// encapsulated pointer. A capsule type can optionally have its own
// implementations of certain operations if it is created with CapsuleWithOps
// instead of Capsule.
//
// The given name is used as the new type's "friendly name". This can be any
// string in principle, but will usually be a short, all-lowercase name aimed
// at users of the embedding language (i.e. not mention Go-specific details)
// and will ideally not create ambiguity with any predefined cty type.
//
// Capsule types are never introduced by any standard cty operation, so a
// calling application opts in to including them within its own type system
// by creating them and introducing them via its own functions. At that point,
// the application is responsible for dealing with any capsule-typed values
// that might be returned.
func Capsule(name string, nativeType reflect.Type) Type {
return Type{
&capsuleType{
Name: name,
GoType: nativeType,
Ops: noCapsuleOps,
},
}
}
// CapsuleWithOps is like Capsule except the caller may provide an object
// representing some overloaded operation implementations to associate with
// the given capsule type.
//
// All of the other caveats and restrictions for capsule types still apply, but
// overloaded operations can potentially help a capsule type participate better
// in cty operations.
func CapsuleWithOps(name string, nativeType reflect.Type, ops *CapsuleOps) Type {
// Copy the operations to make sure the caller can't modify them after
// we're constructed.
ourOps := *ops
ourOps.assertValid()
return Type{
&capsuleType{
Name: name,
GoType: nativeType,
Ops: &ourOps,
},
}
}
// IsCapsuleType returns true if this type is a capsule type, as created
// by cty.Capsule .
func (t Type) IsCapsuleType() bool {
_, ok := t.typeImpl.(*capsuleType)
return ok
}
// EncapsulatedType returns the encapsulated native type of a capsule type,
// or panics if the receiver is not a Capsule type.
//
// Is IsCapsuleType to determine if this method is safe to call.
func (t Type) EncapsulatedType() reflect.Type {
impl, ok := t.typeImpl.(*capsuleType)
if !ok {
panic("not a capsule type")
}
return impl.GoType
}

132
vendor/github.com/zclconf/go-cty/cty/capsule_ops.go generated vendored Normal file
View File

@ -0,0 +1,132 @@
package cty
import (
"reflect"
)
// CapsuleOps represents a set of overloaded operations for a capsule type.
//
// Each field is a reference to a function that can either be nil or can be
// set to an implementation of the corresponding operation. If an operation
// function is nil then it isn't supported for the given capsule type.
type CapsuleOps struct {
// GoString provides the GoString implementation for values of the
// corresponding type. Conventionally this should return a string
// representation of an expression that would produce an equivalent
// value.
GoString func(val interface{}) string
// TypeGoString provides the GoString implementation for the corresponding
// capsule type itself.
TypeGoString func(goTy reflect.Type) string
// Equals provides the implementation of the Equals operation. This is
// called only with known, non-null values of the corresponding type,
// but if the corresponding type is a compound type then it must be
// ready to detect and handle nested unknown or null values, usually
// by recursively calling Value.Equals on those nested values.
//
// The result value must always be of type cty.Bool, or the Equals
// operation will panic.
//
// If RawEquals is set without also setting Equals, the RawEquals
// implementation will be used as a fallback implementation. That fallback
// is appropriate only for leaf types that do not contain any nested
// cty.Value that would need to distinguish Equals vs. RawEquals for their
// own equality.
//
// If RawEquals is nil then Equals must also be nil, selecting the default
// pointer-identity comparison instead.
Equals func(a, b interface{}) Value
// RawEquals provides the implementation of the RawEquals operation.
// This is called only with known, non-null values of the corresponding
// type, but if the corresponding type is a compound type then it must be
// ready to detect and handle nested unknown or null values, usually
// by recursively calling Value.RawEquals on those nested values.
//
// If RawEquals is nil, values of the corresponding type are compared by
// pointer identity of the encapsulated value.
RawEquals func(a, b interface{}) bool
// ConversionFrom can provide conversions from the corresponding type to
// some other type when values of the corresponding type are used with
// the "convert" package. (The main cty package does not use this operation.)
//
// This function itself returns a function, allowing it to switch its
// behavior depending on the given source type. Return nil to indicate
// that no such conversion is available.
ConversionFrom func(src Type) func(interface{}, Path) (Value, error)
// ConversionTo can provide conversions to the corresponding type from
// some other type when values of the corresponding type are used with
// the "convert" package. (The main cty package does not use this operation.)
//
// This function itself returns a function, allowing it to switch its
// behavior depending on the given destination type. Return nil to indicate
// that no such conversion is available.
ConversionTo func(dst Type) func(Value, Path) (interface{}, error)
// ExtensionData is an extension point for applications that wish to
// create their own extension features using capsule types.
//
// The key argument is any value that can be compared with Go's ==
// operator, but should be of a named type in a package belonging to the
// application defining the key. An ExtensionData implementation must
// check to see if the given key is familar to it, and if so return a
// suitable value for the key.
//
// If the given key is unrecognized, the ExtensionData function must
// return a nil interface. (Importantly, not an interface containing a nil
// pointer of some other type.)
// The common implementation of ExtensionData is a single switch statement
// over "key" which has a default case returning nil.
//
// The meaning of any given key is entirely up to the application that
// defines it. Applications consuming ExtensionData from capsule types
// should do so defensively: if the result of ExtensionData is not valid,
// prefer to ignore it or gracefully produce an error rather than causing
// a panic.
ExtensionData func(key interface{}) interface{}
}
// noCapsuleOps is a pointer to a CapsuleOps with no functions set, which
// is used as the default operations value when a type is created using
// the Capsule function.
var noCapsuleOps = &CapsuleOps{}
func (ops *CapsuleOps) assertValid() {
if ops.RawEquals == nil && ops.Equals != nil {
panic("Equals cannot be set without RawEquals")
}
}
// CapsuleOps returns a pointer to the CapsuleOps value for a capsule type,
// or panics if the receiver is not a capsule type.
//
// The caller must not modify the CapsuleOps.
func (ty Type) CapsuleOps() *CapsuleOps {
if !ty.IsCapsuleType() {
panic("not a capsule-typed value")
}
return ty.typeImpl.(*capsuleType).Ops
}
// CapsuleExtensionData is a convenience interface to the ExtensionData
// function that can be optionally implemented for a capsule type. It will
// check to see if the underlying type implements ExtensionData and call it
// if so. If not, it will return nil to indicate that the given key is not
// supported.
//
// See the documentation for CapsuleOps.ExtensionData for more information
// on the purpose of and usage of this mechanism.
//
// If CapsuleExtensionData is called on a non-capsule type then it will panic.
func (ty Type) CapsuleExtensionData(key interface{}) interface{} {
ops := ty.CapsuleOps()
if ops.ExtensionData == nil {
return nil
}
return ops.ExtensionData(key)
}

34
vendor/github.com/zclconf/go-cty/cty/collection.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
package cty
import (
"errors"
)
type collectionTypeImpl interface {
ElementType() Type
}
// IsCollectionType returns true if the given type supports the operations
// that are defined for all collection types.
func (t Type) IsCollectionType() bool {
_, ok := t.typeImpl.(collectionTypeImpl)
return ok
}
// ElementType returns the element type of the receiver if it is a collection
// type, or panics if it is not. Use IsCollectionType first to test whether
// this method will succeed.
func (t Type) ElementType() Type {
if ct, ok := t.typeImpl.(collectionTypeImpl); ok {
return ct.ElementType()
}
panic(errors.New("not a collection type"))
}
// ElementCallback is a callback type used for iterating over elements of
// collections and attributes of objects.
//
// The types of key and value depend on what type is being iterated over.
// Return true to stop iterating after the current element, or false to
// continue iterating.
type ElementCallback func(key Value, val Value) (stop bool)

View 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
}

View 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))
}
}

View 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)
}
}

View 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
}
}

View 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
}

View 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
}
}

View 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")
}
}
},
},
}

View 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
View 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

View 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
View 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)
}

View 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
View 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
}

18
vendor/github.com/zclconf/go-cty/cty/doc.go generated vendored Normal file
View File

@ -0,0 +1,18 @@
// Package cty (pronounced see-tie) provides some infrastructure for a type
// system that might be useful for applications that need to represent
// configuration values provided by the user whose types are not known
// at compile time, particularly if the calling application also allows
// such values to be used in expressions.
//
// The type system consists of primitive types Number, String and Bool, as
// well as List and Map collection types and Object types that can have
// arbitrarily-typed sets of attributes.
//
// A set of operations is defined on these types, which is accessible via
// the wrapper struct Value, which annotates the raw, internal representation
// of a value with its corresponding type.
//
// This package is oriented towards being a building block for configuration
// languages used to bootstrap an application. It is not optimized for use
// in tight loops where CPU time or memory pressure are a concern.
package cty

View File

@ -0,0 +1,194 @@
package cty
import (
"sort"
"github.com/zclconf/go-cty/cty/set"
)
// ElementIterator is the interface type returned by Value.ElementIterator to
// allow the caller to iterate over elements of a collection-typed value.
//
// Its usage pattern is as follows:
//
// it := val.ElementIterator()
// for it.Next() {
// key, val := it.Element()
// // ...
// }
type ElementIterator interface {
Next() bool
Element() (key Value, value Value)
}
func canElementIterator(val Value) bool {
switch {
case val.IsMarked():
return false
case val.ty.IsListType():
return true
case val.ty.IsMapType():
return true
case val.ty.IsSetType():
return true
case val.ty.IsTupleType():
return true
case val.ty.IsObjectType():
return true
default:
return false
}
}
func elementIterator(val Value) ElementIterator {
val.assertUnmarked()
switch {
case val.ty.IsListType():
return &listElementIterator{
ety: val.ty.ElementType(),
vals: val.v.([]interface{}),
idx: -1,
}
case val.ty.IsMapType():
// We iterate the keys in a predictable lexicographical order so
// that results will always be stable given the same input map.
rawMap := val.v.(map[string]interface{})
keys := make([]string, 0, len(rawMap))
for key := range rawMap {
keys = append(keys, key)
}
sort.Strings(keys)
return &mapElementIterator{
ety: val.ty.ElementType(),
vals: rawMap,
keys: keys,
idx: -1,
}
case val.ty.IsSetType():
rawSet := val.v.(set.Set)
return &setElementIterator{
ety: val.ty.ElementType(),
setIt: rawSet.Iterator(),
}
case val.ty.IsTupleType():
return &tupleElementIterator{
etys: val.ty.TupleElementTypes(),
vals: val.v.([]interface{}),
idx: -1,
}
case val.ty.IsObjectType():
// We iterate the keys in a predictable lexicographical order so
// that results will always be stable given the same object type.
atys := val.ty.AttributeTypes()
keys := make([]string, 0, len(atys))
for key := range atys {
keys = append(keys, key)
}
sort.Strings(keys)
return &objectElementIterator{
atys: atys,
vals: val.v.(map[string]interface{}),
attrNames: keys,
idx: -1,
}
default:
panic("attempt to iterate on non-collection, non-tuple type")
}
}
type listElementIterator struct {
ety Type
vals []interface{}
idx int
}
func (it *listElementIterator) Element() (Value, Value) {
i := it.idx
return NumberIntVal(int64(i)), Value{
ty: it.ety,
v: it.vals[i],
}
}
func (it *listElementIterator) Next() bool {
it.idx++
return it.idx < len(it.vals)
}
type mapElementIterator struct {
ety Type
vals map[string]interface{}
keys []string
idx int
}
func (it *mapElementIterator) Element() (Value, Value) {
key := it.keys[it.idx]
return StringVal(key), Value{
ty: it.ety,
v: it.vals[key],
}
}
func (it *mapElementIterator) Next() bool {
it.idx++
return it.idx < len(it.keys)
}
type setElementIterator struct {
ety Type
setIt *set.Iterator
}
func (it *setElementIterator) Element() (Value, Value) {
val := Value{
ty: it.ety,
v: it.setIt.Value(),
}
return val, val
}
func (it *setElementIterator) Next() bool {
return it.setIt.Next()
}
type tupleElementIterator struct {
etys []Type
vals []interface{}
idx int
}
func (it *tupleElementIterator) Element() (Value, Value) {
i := it.idx
return NumberIntVal(int64(i)), Value{
ty: it.etys[i],
v: it.vals[i],
}
}
func (it *tupleElementIterator) Next() bool {
it.idx++
return it.idx < len(it.vals)
}
type objectElementIterator struct {
atys map[string]Type
vals map[string]interface{}
attrNames []string
idx int
}
func (it *objectElementIterator) Element() (Value, Value) {
key := it.attrNames[it.idx]
return StringVal(key), Value{
ty: it.atys[key],
v: it.vals[key],
}
}
func (it *objectElementIterator) Next() bool {
it.idx++
return it.idx < len(it.attrNames)
}

55
vendor/github.com/zclconf/go-cty/cty/error.go generated vendored Normal file
View File

@ -0,0 +1,55 @@
package cty
import (
"fmt"
)
// PathError is a specialization of error that represents where in a
// potentially-deep data structure an error occured, using a Path.
type PathError struct {
error
Path Path
}
func errorf(path Path, f string, args ...interface{}) error {
// We need to copy the Path because often our caller builds it by
// continually mutating the same underlying buffer.
sPath := make(Path, len(path))
copy(sPath, path)
return PathError{
error: fmt.Errorf(f, args...),
Path: sPath,
}
}
// NewErrorf creates a new PathError for the current path by passing the
// given format and arguments to fmt.Errorf and then wrapping the result
// similarly to NewError.
func (p Path) NewErrorf(f string, args ...interface{}) error {
return errorf(p, f, args...)
}
// NewError creates a new PathError for the current path, wrapping the given
// error.
func (p Path) NewError(err error) error {
// if we're being asked to wrap an existing PathError then our new
// PathError will be the concatenation of the two paths, ensuring
// that we still get a single flat PathError that's thus easier for
// callers to deal with.
perr, wrappingPath := err.(PathError)
pathLen := len(p)
if wrappingPath {
pathLen = pathLen + len(perr.Path)
}
sPath := make(Path, pathLen)
copy(sPath, p)
if wrappingPath {
copy(sPath[len(p):], perr.Path)
}
return PathError{
error: err,
Path: sPath,
}
}

View File

@ -0,0 +1,70 @@
package function
import (
"github.com/zclconf/go-cty/cty"
)
// Parameter represents a parameter to a function.
type Parameter struct {
// Name is an optional name for the argument. This package ignores this
// value, but callers may use it for documentation, etc.
Name string
// A type that any argument for this parameter must conform to.
// cty.DynamicPseudoType can be used, either at top-level or nested
// in a parameterized type, to indicate that any type should be
// permitted, to allow the definition of type-generic functions.
Type cty.Type
// If AllowNull is set then null values may be passed into this
// argument's slot in both the type-check function and the implementation
// function. If not set, such values are rejected by the built-in
// checking rules.
AllowNull bool
// If AllowUnknown is set then unknown values may be passed into this
// argument's slot in the implementation function. If not set, any
// unknown values will cause the function to immediately return
// an unkonwn value without calling the implementation function, thus
// freeing the function implementer from dealing with this case.
AllowUnknown bool
// If AllowDynamicType is set then DynamicVal may be passed into this
// argument's slot in the implementation function. If not set, any
// dynamic values will cause the function to immediately return
// DynamicVal value without calling the implementation function, thus
// freeing the function implementer from dealing with this case.
//
// Note that DynamicVal is also unknown, so in order to receive dynamic
// *values* it is also necessary to set AllowUnknown.
//
// However, it is valid to set AllowDynamicType without AllowUnknown, in
// which case a dynamic value may be passed to the type checking function
// but will not make it to the *implementation* function. Instead, an
// unknown value of the type returned by the type-check function will be
// returned. This is suggested for functions that have a static return
// type since it allows the return value to be typed even if the input
// values are not, thus improving the type-check accuracy of derived
// values.
AllowDynamicType bool
// If AllowMarked is set then marked values may be passed into this
// argument's slot in the implementation function. If not set, any
// marked value will be unmarked before calling and then the markings
// from that value will be applied automatically to the function result,
// ensuring that the marks get propagated in a simplistic way even if
// a function is unable to handle them.
//
// For any argument whose parameter has AllowMarked set, it's the
// function implementation's responsibility to Unmark the given value
// and propagate the marks appropriatedly to the result in order to
// avoid losing the marks. Application-specific functions might use
// special rules to selectively propagate particular marks.
//
// The automatic unmarking of values applies only to the main
// implementation function. In an application that uses marked values,
// the Type implementation for a function must always be prepared to accept
// marked values, which is easy to achieve by consulting only the type
// and ignoring the value itself.
AllowMarked bool
}

6
vendor/github.com/zclconf/go-cty/cty/function/doc.go generated vendored Normal file
View File

@ -0,0 +1,6 @@
// Package function builds on the functionality of cty by modeling functions
// that operate on cty Values.
//
// Functions are, at their core, Go anonymous functions. However, this package
// wraps around them utility functions for parameter type checking, etc.
package function

50
vendor/github.com/zclconf/go-cty/cty/function/error.go generated vendored Normal file
View File

@ -0,0 +1,50 @@
package function
import (
"fmt"
"runtime/debug"
)
// ArgError represents an error with one of the arguments in a call. The
// attribute Index represents the zero-based index of the argument in question.
//
// Its error *may* be a cty.PathError, in which case the error actually
// pertains to a nested value within the data structure passed as the argument.
type ArgError struct {
error
Index int
}
func NewArgErrorf(i int, f string, args ...interface{}) error {
return ArgError{
error: fmt.Errorf(f, args...),
Index: i,
}
}
func NewArgError(i int, err error) error {
return ArgError{
error: err,
Index: i,
}
}
// PanicError indicates that a panic occurred while executing either a
// function's type or implementation function. This is captured and wrapped
// into a normal error so that callers (expected to be language runtimes)
// are freed from having to deal with panics in buggy functions.
type PanicError struct {
Value interface{}
Stack []byte
}
func errorForPanic(val interface{}) error {
return PanicError{
Value: val,
Stack: debug.Stack(),
}
}
func (e PanicError) Error() string {
return fmt.Sprintf("panic in function implementation: %s\n%s", e.Value, e.Stack)
}

View File

@ -0,0 +1,342 @@
package function
import (
"fmt"
"github.com/zclconf/go-cty/cty"
)
// Function represents a function. This is the main type in this package.
type Function struct {
spec *Spec
}
// Spec is the specification of a function, used to instantiate
// a new Function.
type Spec struct {
// Params is a description of the positional parameters for the function.
// The standard checking logic rejects any calls that do not provide
// arguments conforming to this definition, freeing the function
// implementer from dealing with such inconsistencies.
Params []Parameter
// VarParam is an optional specification of additional "varargs" the
// function accepts. If this is non-nil then callers may provide an
// arbitrary number of additional arguments (after those matching with
// the fixed parameters in Params) that conform to the given specification,
// which will appear as additional values in the slices of values
// provided to the type and implementation functions.
VarParam *Parameter
// Type is the TypeFunc that decides the return type of the function
// given its arguments, which may be Unknown. See the documentation
// of TypeFunc for more information.
//
// Use StaticReturnType if the function's return type does not vary
// depending on its arguments.
Type TypeFunc
// Impl is the ImplFunc that implements the function's behavior.
//
// Functions are expected to behave as pure functions, and not create
// any visible side-effects.
//
// If a TypeFunc is also provided, the value returned from Impl *must*
// conform to the type it returns, or a call to the function will panic.
Impl ImplFunc
}
// New creates a new function with the given specification.
//
// After passing a Spec to this function, the caller must no longer read from
// or mutate it.
func New(spec *Spec) Function {
f := Function{
spec: spec,
}
return f
}
// TypeFunc is a callback type for determining the return type of a function
// given its arguments.
//
// Any of the values passed to this function may be unknown, even if the
// parameters are not configured to accept unknowns.
//
// If any of the given values are *not* unknown, the TypeFunc may use the
// values for pre-validation and for choosing the return type. For example,
// a hypothetical JSON-unmarshalling function could return
// cty.DynamicPseudoType if the given JSON string is unknown, but return
// a concrete type based on the JSON structure if the JSON string is already
// known.
type TypeFunc func(args []cty.Value) (cty.Type, error)
// ImplFunc is a callback type for the main implementation of a function.
//
// "args" are the values for the arguments, and this slice will always be at
// least as long as the argument definition slice for the function.
//
// "retType" is the type returned from the Type callback, included as a
// convenience to avoid the need to re-compute the return type for generic
// functions whose return type is a function of the arguments.
type ImplFunc func(args []cty.Value, retType cty.Type) (cty.Value, error)
// StaticReturnType returns a TypeFunc that always returns the given type.
//
// This is provided as a convenience for defining a function whose return
// type does not depend on the argument types.
func StaticReturnType(ty cty.Type) TypeFunc {
return func([]cty.Value) (cty.Type, error) {
return ty, nil
}
}
// ReturnType returns the return type of a function given a set of candidate
// argument types, or returns an error if the given types are unacceptable.
//
// If the caller already knows values for at least some of the arguments
// it can be better to call ReturnTypeForValues, since certain functions may
// determine their return types from their values and return DynamicVal if
// the values are unknown.
func (f Function) ReturnType(argTypes []cty.Type) (cty.Type, error) {
vals := make([]cty.Value, len(argTypes))
for i, ty := range argTypes {
vals[i] = cty.UnknownVal(ty)
}
return f.ReturnTypeForValues(vals)
}
// ReturnTypeForValues is similar to ReturnType but can be used if the caller
// already knows the values of some or all of the arguments, in which case
// the function may be able to determine a more definite result if its
// return type depends on the argument *values*.
//
// For any arguments whose values are not known, pass an Unknown value of
// the appropriate type.
func (f Function) ReturnTypeForValues(args []cty.Value) (ty cty.Type, err error) {
var posArgs []cty.Value
var varArgs []cty.Value
if f.spec.VarParam == nil {
if len(args) != len(f.spec.Params) {
return cty.Type{}, fmt.Errorf(
"wrong number of arguments (%d required; %d given)",
len(f.spec.Params), len(args),
)
}
posArgs = args
varArgs = nil
} else {
if len(args) < len(f.spec.Params) {
return cty.Type{}, fmt.Errorf(
"wrong number of arguments (at least %d required; %d given)",
len(f.spec.Params), len(args),
)
}
posArgs = args[0:len(f.spec.Params)]
varArgs = args[len(f.spec.Params):]
}
for i, spec := range f.spec.Params {
val := posArgs[i]
if val.IsMarked() && !spec.AllowMarked {
// During type checking we just unmark values and discard their
// marks, under the assumption that during actual execution of
// the function we'll do similarly and then re-apply the marks
// afterwards. Note that this does mean that a function that
// inspects values (rather than just types) in its Type
// implementation can potentially fail to take into account marks,
// unless it specifically opts in to seeing them.
unmarked, _ := val.Unmark()
newArgs := make([]cty.Value, len(args))
copy(newArgs, args)
newArgs[i] = unmarked
args = newArgs
}
if val.IsNull() && !spec.AllowNull {
return cty.Type{}, NewArgErrorf(i, "argument must not be null")
}
// AllowUnknown is ignored for type-checking, since we expect to be
// able to type check with unknown values. We *do* still need to deal
// with DynamicPseudoType here though, since the Type function might
// not be ready to deal with that.
if val.Type() == cty.DynamicPseudoType {
if !spec.AllowDynamicType {
return cty.DynamicPseudoType, nil
}
} else if errs := val.Type().TestConformance(spec.Type); errs != nil {
// For now we'll just return the first error in the set, since
// we don't have a good way to return the whole list here.
// Would be good to do something better at some point...
return cty.Type{}, NewArgError(i, errs[0])
}
}
if varArgs != nil {
spec := f.spec.VarParam
for i, val := range varArgs {
realI := i + len(posArgs)
if val.IsMarked() && !spec.AllowMarked {
// See the similar block in the loop above for what's going on here.
unmarked, _ := val.Unmark()
newArgs := make([]cty.Value, len(args))
copy(newArgs, args)
newArgs[realI] = unmarked
args = newArgs
}
if val.IsNull() && !spec.AllowNull {
return cty.Type{}, NewArgErrorf(realI, "argument must not be null")
}
if val.Type() == cty.DynamicPseudoType {
if !spec.AllowDynamicType {
return cty.DynamicPseudoType, nil
}
} else if errs := val.Type().TestConformance(spec.Type); errs != nil {
// For now we'll just return the first error in the set, since
// we don't have a good way to return the whole list here.
// Would be good to do something better at some point...
return cty.Type{}, NewArgError(i, errs[0])
}
}
}
// Intercept any panics from the function and return them as normal errors,
// so a calling language runtime doesn't need to deal with panics.
defer func() {
if r := recover(); r != nil {
ty = cty.NilType
err = errorForPanic(r)
}
}()
return f.spec.Type(args)
}
// Call actually calls the function with the given arguments, which must
// conform to the function's parameter specification or an error will be
// returned.
func (f Function) Call(args []cty.Value) (val cty.Value, err error) {
expectedType, err := f.ReturnTypeForValues(args)
if err != nil {
return cty.NilVal, err
}
// Type checking already dealt with most situations relating to our
// parameter specification, but we still need to deal with unknown
// values and marked values.
posArgs := args[:len(f.spec.Params)]
varArgs := args[len(f.spec.Params):]
var resultMarks []cty.ValueMarks
for i, spec := range f.spec.Params {
val := posArgs[i]
if !val.IsKnown() && !spec.AllowUnknown {
return cty.UnknownVal(expectedType), nil
}
if val.IsMarked() && !spec.AllowMarked {
unwrappedVal, marks := val.Unmark()
// In order to avoid additional overhead on applications that
// are not using marked values, we copy the given args only
// if we encounter a marked value we need to unmark. However,
// as a consequence we end up doing redundant copying if multiple
// marked values need to be unwrapped. That seems okay because
// argument lists are generally small.
newArgs := make([]cty.Value, len(args))
copy(newArgs, args)
newArgs[i] = unwrappedVal
resultMarks = append(resultMarks, marks)
args = newArgs
}
}
if f.spec.VarParam != nil {
spec := f.spec.VarParam
for i, val := range varArgs {
if !val.IsKnown() && !spec.AllowUnknown {
return cty.UnknownVal(expectedType), nil
}
if val.IsMarked() && !spec.AllowMarked {
unwrappedVal, marks := val.Unmark()
newArgs := make([]cty.Value, len(args))
copy(newArgs, args)
newArgs[len(posArgs)+i] = unwrappedVal
resultMarks = append(resultMarks, marks)
args = newArgs
}
}
}
var retVal cty.Value
{
// Intercept any panics from the function and return them as normal errors,
// so a calling language runtime doesn't need to deal with panics.
defer func() {
if r := recover(); r != nil {
val = cty.NilVal
err = errorForPanic(r)
}
}()
retVal, err = f.spec.Impl(args, expectedType)
if err != nil {
return cty.NilVal, err
}
if len(resultMarks) > 0 {
retVal = retVal.WithMarks(resultMarks...)
}
}
// Returned value must conform to what the Type function expected, to
// protect callers from having to deal with inconsistencies.
if errs := retVal.Type().TestConformance(expectedType); errs != nil {
panic(fmt.Errorf(
"returned value %#v does not conform to expected return type %#v: %s",
retVal, expectedType, errs[0],
))
}
return retVal, nil
}
// ProxyFunc the type returned by the method Function.Proxy.
type ProxyFunc func(args ...cty.Value) (cty.Value, error)
// Proxy returns a function that can be called with cty.Value arguments
// to run the function. This is provided as a convenience for when using
// a function directly within Go code.
func (f Function) Proxy() ProxyFunc {
return func(args ...cty.Value) (cty.Value, error) {
return f.Call(args)
}
}
// Params returns information about the function's fixed positional parameters.
// This does not include information about any variadic arguments accepted;
// for that, call VarParam.
func (f Function) Params() []Parameter {
new := make([]Parameter, len(f.spec.Params))
copy(new, f.spec.Params)
return new
}
// VarParam returns information about the variadic arguments the function
// expects, or nil if the function is not variadic.
func (f Function) VarParam() *Parameter {
if f.spec.VarParam == nil {
return nil
}
ret := *f.spec.VarParam
return &ret
}

View File

@ -0,0 +1,31 @@
package function
import (
"github.com/zclconf/go-cty/cty"
)
// Unpredictable wraps a given function such that it retains the same arguments
// and type checking behavior but will return an unknown value when called.
//
// It is recommended that most functions be "pure", which is to say that they
// will always produce the same value given particular input. However,
// sometimes it is necessary to offer functions whose behavior depends on
// some external state, such as reading a file or determining the current time.
// In such cases, an unpredictable wrapper might be used to stand in for
// the function during some sort of prior "checking" phase in order to delay
// the actual effect until later.
//
// While Unpredictable can support a function that isn't pure in its
// implementation, it still expects a function to be pure in its type checking
// behavior, except for the special case of returning cty.DynamicPseudoType
// if it is not yet able to predict its return value based on current argument
// information.
func Unpredictable(f Function) Function {
newSpec := *f.spec // shallow copy
newSpec.Impl = unpredictableImpl
return New(&newSpec)
}
func unpredictableImpl(args []cty.Value, retType cty.Type) (cty.Value, error) {
return cty.UnknownVal(retType), nil
}

204
vendor/github.com/zclconf/go-cty/cty/gob.go generated vendored Normal file
View File

@ -0,0 +1,204 @@
package cty
import (
"bytes"
"encoding/gob"
"errors"
"fmt"
"math/big"
"github.com/zclconf/go-cty/cty/set"
)
// GobEncode is an implementation of the gob.GobEncoder interface, which
// allows Values to be included in structures encoded with encoding/gob.
//
// Currently it is not possible to represent values of capsule types in gob,
// because the types themselves cannot be represented.
func (val Value) GobEncode() ([]byte, error) {
if val.IsMarked() {
return nil, errors.New("value is marked")
}
buf := &bytes.Buffer{}
enc := gob.NewEncoder(buf)
gv := gobValue{
Version: 0,
Ty: val.ty,
V: val.v,
}
err := enc.Encode(gv)
if err != nil {
return nil, fmt.Errorf("error encoding cty.Value: %s", err)
}
return buf.Bytes(), nil
}
// GobDecode is an implementation of the gob.GobDecoder interface, which
// inverts the operation performed by GobEncode. See the documentation of
// GobEncode for considerations when using cty.Value instances with gob.
func (val *Value) GobDecode(buf []byte) error {
r := bytes.NewReader(buf)
dec := gob.NewDecoder(r)
var gv gobValue
err := dec.Decode(&gv)
if err != nil {
return fmt.Errorf("error decoding cty.Value: %s", err)
}
if gv.Version != 0 {
return fmt.Errorf("unsupported cty.Value encoding version %d; only 0 is supported", gv.Version)
}
// Because big.Float.GobEncode is implemented with a pointer reciever,
// gob encoding of an interface{} containing a *big.Float value does not
// round-trip correctly, emerging instead as a non-pointer big.Float.
// The rest of cty expects all number values to be represented by
// *big.Float, so we'll fix that up here.
gv.V = gobDecodeFixNumberPtr(gv.V, gv.Ty)
val.ty = gv.Ty
val.v = gv.V
return nil
}
// GobEncode is an implementation of the gob.GobEncoder interface, which
// allows Types to be included in structures encoded with encoding/gob.
//
// Currently it is not possible to represent capsule types in gob.
func (t Type) GobEncode() ([]byte, error) {
buf := &bytes.Buffer{}
enc := gob.NewEncoder(buf)
gt := gobType{
Version: 0,
Impl: t.typeImpl,
}
err := enc.Encode(gt)
if err != nil {
return nil, fmt.Errorf("error encoding cty.Type: %s", err)
}
return buf.Bytes(), nil
}
// GobDecode is an implementatino of the gob.GobDecoder interface, which
// reverses the encoding performed by GobEncode to allow types to be recovered
// from gob buffers.
func (t *Type) GobDecode(buf []byte) error {
r := bytes.NewReader(buf)
dec := gob.NewDecoder(r)
var gt gobType
err := dec.Decode(&gt)
if err != nil {
return fmt.Errorf("error decoding cty.Type: %s", err)
}
if gt.Version != 0 {
return fmt.Errorf("unsupported cty.Type encoding version %d; only 0 is supported", gt.Version)
}
t.typeImpl = gt.Impl
return nil
}
// Capsule types cannot currently be gob-encoded, because they rely on pointer
// equality and we have no way to recover the original pointer on decode.
func (t *capsuleType) GobEncode() ([]byte, error) {
return nil, fmt.Errorf("cannot gob-encode capsule type %q", t.FriendlyName(friendlyTypeName))
}
func (t *capsuleType) GobDecode() ([]byte, error) {
return nil, fmt.Errorf("cannot gob-decode capsule type %q", t.FriendlyName(friendlyTypeName))
}
type gobValue struct {
Version int
Ty Type
V interface{}
}
type gobType struct {
Version int
Impl typeImpl
}
type gobCapsuleTypeImpl struct {
}
// goDecodeFixNumberPtr fixes an unfortunate quirk of round-tripping cty.Number
// values through gob: the big.Float.GobEncode method is implemented on a
// pointer receiver, and so it loses the "pointer-ness" of the value on
// encode, causing the values to emerge the other end as big.Float rather than
// *big.Float as we expect elsewhere in cty.
//
// The implementation of gobDecodeFixNumberPtr mutates the given raw value
// during its work, and may either return the same value mutated or a new
// value. Callers must no longer use whatever value they pass as "raw" after
// this function is called.
func gobDecodeFixNumberPtr(raw interface{}, ty Type) interface{} {
// Unfortunately we need to work recursively here because number values
// might be embedded in structural or collection type values.
switch {
case ty.Equals(Number):
if bf, ok := raw.(big.Float); ok {
return &bf // wrap in pointer
}
case ty.IsMapType() && ty.ElementType().Equals(Number):
if m, ok := raw.(map[string]interface{}); ok {
for k, v := range m {
m[k] = gobDecodeFixNumberPtr(v, ty.ElementType())
}
}
case ty.IsListType() && ty.ElementType().Equals(Number):
if s, ok := raw.([]interface{}); ok {
for i, v := range s {
s[i] = gobDecodeFixNumberPtr(v, ty.ElementType())
}
}
case ty.IsSetType() && ty.ElementType().Equals(Number):
if s, ok := raw.(set.Set); ok {
newS := set.NewSet(s.Rules())
for it := s.Iterator(); it.Next(); {
newV := gobDecodeFixNumberPtr(it.Value(), ty.ElementType())
newS.Add(newV)
}
return newS
}
case ty.IsObjectType():
if m, ok := raw.(map[string]interface{}); ok {
for k, v := range m {
aty := ty.AttributeType(k)
m[k] = gobDecodeFixNumberPtr(v, aty)
}
}
case ty.IsTupleType():
if s, ok := raw.([]interface{}); ok {
for i, v := range s {
ety := ty.TupleElementType(i)
s[i] = gobDecodeFixNumberPtr(v, ety)
}
}
}
return raw
}
// gobDecodeFixNumberPtrVal is a helper wrapper around gobDecodeFixNumberPtr
// that works with already-constructed values. This is primarily for testing,
// to fix up intentionally-invalid number values for the parts of the test
// code that need them to be valid, such as calling GoString on them.
func gobDecodeFixNumberPtrVal(v Value) Value {
raw := gobDecodeFixNumberPtr(v.v, v.ty)
return Value{
v: raw,
ty: v.ty,
}
}

99
vendor/github.com/zclconf/go-cty/cty/helper.go generated vendored Normal file
View File

@ -0,0 +1,99 @@
package cty
import (
"fmt"
)
// anyUnknown is a helper to easily check if a set of values contains any
// unknowns, for operations that short-circuit to return unknown in that case.
func anyUnknown(values ...Value) bool {
for _, val := range values {
if val.v == unknown {
return true
}
}
return false
}
// typeCheck tests whether all of the given values belong to the given type.
// If the given types are a mixture of the given type and the dynamic
// pseudo-type then a short-circuit dynamic value is returned. If the given
// values are all of the correct type but at least one is unknown then
// a short-circuit unknown value is returned. If any other types appear then
// an error is returned. Otherwise (finally!) the result is nil, nil.
func typeCheck(required Type, ret Type, values ...Value) (shortCircuit *Value, err error) {
hasDynamic := false
hasUnknown := false
for i, val := range values {
if val.ty == DynamicPseudoType {
hasDynamic = true
continue
}
if !val.Type().Equals(required) {
return nil, fmt.Errorf(
"type mismatch: want %s but value %d is %s",
required.FriendlyName(),
i, val.ty.FriendlyName(),
)
}
if val.v == unknown {
hasUnknown = true
}
}
if hasDynamic {
return &DynamicVal, nil
}
if hasUnknown {
ret := UnknownVal(ret)
return &ret, nil
}
return nil, nil
}
// mustTypeCheck is a wrapper around typeCheck that immediately panics if
// any error is returned.
func mustTypeCheck(required Type, ret Type, values ...Value) *Value {
shortCircuit, err := typeCheck(required, ret, values...)
if err != nil {
panic(err)
}
return shortCircuit
}
// shortCircuitForceType takes the return value from mustTypeCheck and
// replaces it with an unknown of the given type if the original value was
// DynamicVal.
//
// This is useful for operations that are specified to always return a
// particular type, since then a dynamic result can safely be "upgrade" to
// a strongly-typed unknown, which then allows subsequent operations to
// be actually type-checked.
//
// It is safe to use this only if the operation in question is defined as
// returning either a value of the given type or panicking, since we know
// then that subsequent operations won't run if the operation panics.
//
// If the given short-circuit value is *not* DynamicVal then it must be
// of the given type, or this function will panic.
func forceShortCircuitType(shortCircuit *Value, ty Type) *Value {
if shortCircuit == nil {
return nil
}
if shortCircuit.ty == DynamicPseudoType {
ret := UnknownVal(ty)
return &ret
}
if !shortCircuit.ty.Equals(ty) {
panic("forceShortCircuitType got value of wrong type")
}
return shortCircuit
}

176
vendor/github.com/zclconf/go-cty/cty/json.go generated vendored Normal file
View File

@ -0,0 +1,176 @@
package cty
import (
"bytes"
"encoding/json"
"fmt"
)
// MarshalJSON is an implementation of json.Marshaler that allows Type
// instances to be serialized as JSON.
//
// All standard types can be serialized, but capsule types cannot since there
// is no way to automatically recover the original pointer and capsule types
// compare by equality.
func (t Type) MarshalJSON() ([]byte, error) {
switch impl := t.typeImpl.(type) {
case primitiveType:
switch impl.Kind {
case primitiveTypeBool:
return []byte{'"', 'b', 'o', 'o', 'l', '"'}, nil
case primitiveTypeNumber:
return []byte{'"', 'n', 'u', 'm', 'b', 'e', 'r', '"'}, nil
case primitiveTypeString:
return []byte{'"', 's', 't', 'r', 'i', 'n', 'g', '"'}, nil
default:
panic("unknown primitive type kind")
}
case typeList, typeMap, typeSet:
buf := &bytes.Buffer{}
etyJSON, err := t.ElementType().MarshalJSON()
if err != nil {
return nil, err
}
buf.WriteRune('[')
switch impl.(type) {
case typeList:
buf.WriteString(`"list"`)
case typeMap:
buf.WriteString(`"map"`)
case typeSet:
buf.WriteString(`"set"`)
}
buf.WriteRune(',')
buf.Write(etyJSON)
buf.WriteRune(']')
return buf.Bytes(), nil
case typeObject:
buf := &bytes.Buffer{}
atysJSON, err := json.Marshal(t.AttributeTypes())
if err != nil {
return nil, err
}
buf.WriteString(`["object",`)
buf.Write(atysJSON)
buf.WriteRune(']')
return buf.Bytes(), nil
case typeTuple:
buf := &bytes.Buffer{}
etysJSON, err := json.Marshal(t.TupleElementTypes())
if err != nil {
return nil, err
}
buf.WriteString(`["tuple",`)
buf.Write(etysJSON)
buf.WriteRune(']')
return buf.Bytes(), nil
case pseudoTypeDynamic:
return []byte{'"', 'd', 'y', 'n', 'a', 'm', 'i', 'c', '"'}, nil
case *capsuleType:
return nil, fmt.Errorf("type not allowed: %s", t.FriendlyName())
default:
// should never happen
panic("unknown type implementation")
}
}
// UnmarshalJSON is the opposite of MarshalJSON. See the documentation of
// MarshalJSON for information on the limitations of JSON serialization of
// types.
func (t *Type) UnmarshalJSON(buf []byte) error {
r := bytes.NewReader(buf)
dec := json.NewDecoder(r)
tok, err := dec.Token()
if err != nil {
return err
}
switch v := tok.(type) {
case string:
switch v {
case "bool":
*t = Bool
case "number":
*t = Number
case "string":
*t = String
case "dynamic":
*t = DynamicPseudoType
default:
return fmt.Errorf("invalid primitive type name %q", v)
}
if dec.More() {
return fmt.Errorf("extraneous data after type description")
}
return nil
case json.Delim:
if rune(v) != '[' {
return fmt.Errorf("invalid complex type description")
}
tok, err = dec.Token()
if err != nil {
return err
}
kind, ok := tok.(string)
if !ok {
return fmt.Errorf("invalid complex type kind name")
}
switch kind {
case "list":
var ety Type
err = dec.Decode(&ety)
if err != nil {
return err
}
*t = List(ety)
case "map":
var ety Type
err = dec.Decode(&ety)
if err != nil {
return err
}
*t = Map(ety)
case "set":
var ety Type
err = dec.Decode(&ety)
if err != nil {
return err
}
*t = Set(ety)
case "object":
var atys map[string]Type
err = dec.Decode(&atys)
if err != nil {
return err
}
*t = Object(atys)
case "tuple":
var etys []Type
err = dec.Decode(&etys)
if err != nil {
return err
}
*t = Tuple(etys)
default:
return fmt.Errorf("invalid complex type kind name")
}
tok, err = dec.Token()
if err != nil {
return err
}
if delim, ok := tok.(json.Delim); !ok || rune(delim) != ']' || dec.More() {
return fmt.Errorf("unexpected extra data in type description")
}
return nil
default:
return fmt.Errorf("invalid type description")
}
}

74
vendor/github.com/zclconf/go-cty/cty/list_type.go generated vendored Normal file
View File

@ -0,0 +1,74 @@
package cty
import (
"fmt"
)
// TypeList instances represent specific list types. Each distinct ElementType
// creates a distinct, non-equal list type.
type typeList struct {
typeImplSigil
ElementTypeT Type
}
// List creates a map type with the given element Type.
//
// List types are CollectionType implementations.
func List(elem Type) Type {
return Type{
typeList{
ElementTypeT: elem,
},
}
}
// Equals returns true if the other Type is a list whose element type is
// equal to that of the receiver.
func (t typeList) Equals(other Type) bool {
ot, isList := other.typeImpl.(typeList)
if !isList {
return false
}
return t.ElementTypeT.Equals(ot.ElementTypeT)
}
func (t typeList) FriendlyName(mode friendlyTypeNameMode) string {
elemName := t.ElementTypeT.friendlyNameMode(mode)
if mode == friendlyTypeConstraintName {
if t.ElementTypeT == DynamicPseudoType {
elemName = "any single type"
}
}
return "list of " + elemName
}
func (t typeList) ElementType() Type {
return t.ElementTypeT
}
func (t typeList) GoString() string {
return fmt.Sprintf("cty.List(%#v)", t.ElementTypeT)
}
// IsListType returns true if the given type is a list type, regardless of its
// element type.
func (t Type) IsListType() bool {
_, ok := t.typeImpl.(typeList)
return ok
}
// ListElementType is a convenience method that checks if the given type is
// a list type, returning a pointer to its element type if so and nil
// otherwise. This is intended to allow convenient conditional branches,
// like so:
//
// if et := t.ListElementType(); et != nil {
// // Do something with *et
// }
func (t Type) ListElementType() *Type {
if lt, ok := t.typeImpl.(typeList); ok {
return &lt.ElementTypeT
}
return nil
}

74
vendor/github.com/zclconf/go-cty/cty/map_type.go generated vendored Normal file
View File

@ -0,0 +1,74 @@
package cty
import (
"fmt"
)
// TypeList instances represent specific list types. Each distinct ElementType
// creates a distinct, non-equal list type.
type typeMap struct {
typeImplSigil
ElementTypeT Type
}
// Map creates a map type with the given element Type.
//
// Map types are CollectionType implementations.
func Map(elem Type) Type {
return Type{
typeMap{
ElementTypeT: elem,
},
}
}
// Equals returns true if the other Type is a map whose element type is
// equal to that of the receiver.
func (t typeMap) Equals(other Type) bool {
ot, isMap := other.typeImpl.(typeMap)
if !isMap {
return false
}
return t.ElementTypeT.Equals(ot.ElementTypeT)
}
func (t typeMap) FriendlyName(mode friendlyTypeNameMode) string {
elemName := t.ElementTypeT.friendlyNameMode(mode)
if mode == friendlyTypeConstraintName {
if t.ElementTypeT == DynamicPseudoType {
elemName = "any single type"
}
}
return "map of " + elemName
}
func (t typeMap) ElementType() Type {
return t.ElementTypeT
}
func (t typeMap) GoString() string {
return fmt.Sprintf("cty.Map(%#v)", t.ElementTypeT)
}
// IsMapType returns true if the given type is a list type, regardless of its
// element type.
func (t Type) IsMapType() bool {
_, ok := t.typeImpl.(typeMap)
return ok
}
// MapElementType is a convenience method that checks if the given type is
// a map type, returning a pointer to its element type if so and nil
// otherwise. This is intended to allow convenient conditional branches,
// like so:
//
// if et := t.MapElementType(); et != nil {
// // Do something with *et
// }
func (t Type) MapElementType() *Type {
if lt, ok := t.typeImpl.(typeMap); ok {
return &lt.ElementTypeT
}
return nil
}

296
vendor/github.com/zclconf/go-cty/cty/marks.go generated vendored Normal file
View File

@ -0,0 +1,296 @@
package cty
import (
"fmt"
"strings"
)
// marker is an internal wrapper type used to add special "marks" to values.
//
// A "mark" is an annotation that can be used to represent additional
// characteristics of values that propagate through operation methods to
// result values. However, a marked value cannot be used with integration
// methods normally associated with its type, in order to ensure that
// calling applications don't inadvertently drop marks as they round-trip
// values out of cty and back in again.
//
// Marked values are created only explicitly by the calling application, so
// an application that never marks a value does not need to worry about
// encountering marked values.
type marker struct {
realV interface{}
marks ValueMarks
}
// ValueMarks is a map, representing a set, of "mark" values associated with
// a Value. See Value.Mark for more information on the usage of mark values.
type ValueMarks map[interface{}]struct{}
// NewValueMarks constructs a new ValueMarks set with the given mark values.
func NewValueMarks(marks ...interface{}) ValueMarks {
if len(marks) == 0 {
return nil
}
ret := make(ValueMarks, len(marks))
for _, v := range marks {
ret[v] = struct{}{}
}
return ret
}
// Equal returns true if the receiver and the given ValueMarks both contain
// the same marks.
func (m ValueMarks) Equal(o ValueMarks) bool {
if len(m) != len(o) {
return false
}
for v := range m {
if _, ok := o[v]; !ok {
return false
}
}
return true
}
func (m ValueMarks) GoString() string {
var s strings.Builder
s.WriteString("cty.NewValueMarks(")
i := 0
for mv := range m {
if i != 0 {
s.WriteString(", ")
}
s.WriteString(fmt.Sprintf("%#v", mv))
i++
}
s.WriteString(")")
return s.String()
}
// IsMarked returns true if and only if the receiving value carries at least
// one mark. A marked value cannot be used directly with integration methods
// without explicitly unmarking it (and retrieving the markings) first.
func (val Value) IsMarked() bool {
_, ok := val.v.(marker)
return ok
}
// HasMark returns true if and only if the receiving value has the given mark.
func (val Value) HasMark(mark interface{}) bool {
if mr, ok := val.v.(marker); ok {
_, ok := mr.marks[mark]
return ok
}
return false
}
// ContainsMarked returns true if the receiving value or any value within it
// is marked.
//
// This operation is relatively expensive. If you only need a shallow result,
// use IsMarked instead.
func (val Value) ContainsMarked() bool {
ret := false
Walk(val, func(_ Path, v Value) (bool, error) {
if v.IsMarked() {
ret = true
return false, nil
}
return true, nil
})
return ret
}
func (val Value) assertUnmarked() {
if val.IsMarked() {
panic("value is marked, so must be unmarked first")
}
}
// Marks returns a map (representing a set) of all of the mark values
// associated with the receiving value, without changing the marks. Returns nil
// if the value is not marked at all.
func (val Value) Marks() ValueMarks {
if mr, ok := val.v.(marker); ok {
// copy so that the caller can't mutate our internals
ret := make(ValueMarks, len(mr.marks))
for k, v := range mr.marks {
ret[k] = v
}
return ret
}
return nil
}
// HasSameMarks returns true if an only if the receiver and the given other
// value have identical marks.
func (val Value) HasSameMarks(other Value) bool {
vm, vmOK := val.v.(marker)
om, omOK := other.v.(marker)
if vmOK != omOK {
return false
}
if vmOK {
return vm.marks.Equal(om.marks)
}
return true
}
// Mark returns a new value that as the same type and underlying value as
// the receiver but that also carries the given value as a "mark".
//
// Marks are used to carry additional application-specific characteristics
// associated with values. A marked value can be used with operation methods,
// in which case the marks are propagated to the operation results. A marked
// value _cannot_ be used with integration methods, so callers of those
// must derive an unmarked value using Unmark (and thus explicitly handle
// the markings) before calling the integration methods.
//
// The mark value can be any value that would be valid to use as a map key.
// The mark value should be of a named type in order to use the type itself
// as a namespace for markings. That type can be unexported if desired, in
// order to ensure that the mark can only be handled through the defining
// package's own functions.
//
// An application that never calls this method does not need to worry about
// handling marked values.
func (val Value) Mark(mark interface{}) Value {
var newMarker marker
newMarker.realV = val.v
if mr, ok := val.v.(marker); ok {
// It's already a marker, so we'll retain existing marks.
newMarker.marks = make(ValueMarks, len(mr.marks)+1)
for k, v := range mr.marks {
newMarker.marks[k] = v
}
} else {
// It's not a marker yet, so we're creating the first mark.
newMarker.marks = make(ValueMarks, 1)
}
newMarker.marks[mark] = struct{}{}
return Value{
ty: val.ty,
v: newMarker,
}
}
// Unmark separates the marks of the receiving value from the value itself,
// removing a new unmarked value and a map (representing a set) of the marks.
//
// If the receiver isn't marked, Unmark returns it verbatim along with a nil
// map of marks.
func (val Value) Unmark() (Value, ValueMarks) {
if !val.IsMarked() {
return val, nil
}
mr := val.v.(marker)
marks := val.Marks() // copy so that the caller can't mutate our internals
return Value{
ty: val.ty,
v: mr.realV,
}, marks
}
// UnmarkDeep is similar to Unmark, but it works with an entire nested structure
// rather than just the given value directly.
//
// The result is guaranteed to contain no nested values that are marked, and
// the returned marks set includes the superset of all of the marks encountered
// during the operation.
func (val Value) UnmarkDeep() (Value, ValueMarks) {
marks := make(ValueMarks)
ret, _ := Transform(val, func(_ Path, v Value) (Value, error) {
unmarkedV, valueMarks := val.Unmark()
for m, s := range valueMarks {
marks[m] = s
}
return unmarkedV, nil
})
return ret, marks
}
func (val Value) unmarkForce() Value {
unw, _ := val.Unmark()
return unw
}
// WithMarks returns a new value that has the same type and underlying value
// as the receiver and also has the marks from the given maps (representing
// sets).
func (val Value) WithMarks(marks ...ValueMarks) Value {
if len(marks) == 0 {
return val
}
ownMarks := val.Marks()
markCount := len(ownMarks)
for _, s := range marks {
markCount += len(s)
}
if markCount == 0 {
return val
}
newMarks := make(ValueMarks, markCount)
for m := range ownMarks {
newMarks[m] = struct{}{}
}
for _, s := range marks {
for m := range s {
newMarks[m] = struct{}{}
}
}
v := val.v
if mr, ok := v.(marker); ok {
v = mr.realV
}
return Value{
ty: val.ty,
v: marker{
realV: v,
marks: newMarks,
},
}
}
// WithSameMarks returns a new value that has the same type and underlying
// value as the receiver and also has the marks from the given source values.
//
// Use this if you are implementing your own higher-level operations against
// cty using the integration methods, to re-introduce the marks from the
// source values of the operation.
func (val Value) WithSameMarks(srcs ...Value) Value {
if len(srcs) == 0 {
return val
}
ownMarks := val.Marks()
markCount := len(ownMarks)
for _, sv := range srcs {
if mr, ok := sv.v.(marker); ok {
markCount += len(mr.marks)
}
}
if markCount == 0 {
return val
}
newMarks := make(ValueMarks, markCount)
for m := range ownMarks {
newMarks[m] = struct{}{}
}
for _, sv := range srcs {
if mr, ok := sv.v.(marker); ok {
for m := range mr.marks {
newMarks[m] = struct{}{}
}
}
}
v := val.v
if mr, ok := v.(marker); ok {
v = mr.realV
}
return Value{
ty: val.ty,
v: marker{
realV: v,
marks: newMarks,
},
}
}

14
vendor/github.com/zclconf/go-cty/cty/null.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
package cty
// NullVal returns a null value of the given type. A null can be created of any
// type, but operations on such values will always panic. Calling applications
// are encouraged to use nulls only sparingly, particularly when user-provided
// expressions are to be evaluated, since the precence of nulls creates a
// much higher chance of evaluation errors that can't be caught by a type
// checker.
func NullVal(t Type) Value {
return Value{
ty: t,
v: nil,
}
}

135
vendor/github.com/zclconf/go-cty/cty/object_type.go generated vendored Normal file
View File

@ -0,0 +1,135 @@
package cty
import (
"fmt"
)
type typeObject struct {
typeImplSigil
AttrTypes map[string]Type
}
// Object creates an object type with the given attribute types.
//
// After a map is passed to this function the caller must no longer access it,
// since ownership is transferred to this library.
func Object(attrTypes map[string]Type) Type {
attrTypesNorm := make(map[string]Type, len(attrTypes))
for k, v := range attrTypes {
attrTypesNorm[NormalizeString(k)] = v
}
return Type{
typeObject{
AttrTypes: attrTypesNorm,
},
}
}
func (t typeObject) Equals(other Type) bool {
if ot, ok := other.typeImpl.(typeObject); ok {
if len(t.AttrTypes) != len(ot.AttrTypes) {
// Fast path: if we don't have the same number of attributes
// then we can't possibly be equal. This also avoids the need
// to test attributes in both directions below, since we know
// there can't be extras in "other".
return false
}
for attr, ty := range t.AttrTypes {
oty, ok := ot.AttrTypes[attr]
if !ok {
return false
}
if !oty.Equals(ty) {
return false
}
}
return true
}
return false
}
func (t typeObject) FriendlyName(mode friendlyTypeNameMode) string {
// There isn't really a friendly way to write an object type due to its
// complexity, so we'll just do something English-ish. Callers will
// probably want to make some extra effort to avoid ever printing out
// an object type FriendlyName in its entirety. For example, could
// produce an error message by diffing two object types and saying
// something like "Expected attribute foo to be string, but got number".
// TODO: Finish this
return "object"
}
func (t typeObject) GoString() string {
if len(t.AttrTypes) == 0 {
return "cty.EmptyObject"
}
return fmt.Sprintf("cty.Object(%#v)", t.AttrTypes)
}
// EmptyObject is a shorthand for Object(map[string]Type{}), to more
// easily talk about the empty object type.
var EmptyObject Type
// EmptyObjectVal is the only possible non-null, non-unknown value of type
// EmptyObject.
var EmptyObjectVal Value
func init() {
EmptyObject = Object(map[string]Type{})
EmptyObjectVal = Value{
ty: EmptyObject,
v: map[string]interface{}{},
}
}
// IsObjectType returns true if the given type is an object type, regardless
// of its element type.
func (t Type) IsObjectType() bool {
_, ok := t.typeImpl.(typeObject)
return ok
}
// HasAttribute returns true if the receiver has an attribute with the given
// name, regardless of its type. Will panic if the reciever isn't an object
// type; use IsObjectType to determine whether this operation will succeed.
func (t Type) HasAttribute(name string) bool {
name = NormalizeString(name)
if ot, ok := t.typeImpl.(typeObject); ok {
_, hasAttr := ot.AttrTypes[name]
return hasAttr
}
panic("HasAttribute on non-object Type")
}
// AttributeType returns the type of the attribute with the given name. Will
// panic if the receiver is not an object type (use IsObjectType to confirm)
// or if the object type has no such attribute (use HasAttribute to confirm).
func (t Type) AttributeType(name string) Type {
name = NormalizeString(name)
if ot, ok := t.typeImpl.(typeObject); ok {
aty, hasAttr := ot.AttrTypes[name]
if !hasAttr {
panic("no such attribute")
}
return aty
}
panic("AttributeType on non-object Type")
}
// AttributeTypes returns a map from attribute names to their associated
// types. Will panic if the receiver is not an object type (use IsObjectType
// to confirm).
//
// The returned map is part of the internal state of the type, and is provided
// for read access only. It is forbidden for any caller to modify the returned
// map. For many purposes the attribute-related methods of Value are more
// appropriate and more convenient to use.
func (t Type) AttributeTypes() map[string]Type {
if ot, ok := t.typeImpl.(typeObject); ok {
return ot.AttrTypes
}
panic("AttributeTypes on non-object Type")
}

250
vendor/github.com/zclconf/go-cty/cty/path.go generated vendored Normal file
View File

@ -0,0 +1,250 @@
package cty
import (
"errors"
"fmt"
)
// A Path is a sequence of operations to locate a nested value within a
// data structure.
//
// The empty Path represents the given item. Any PathSteps within represent
// taking a single step down into a data structure.
//
// Path has some convenience methods for gradually constructing a path,
// but callers can also feel free to just produce a slice of PathStep manually
// and convert to this type, which may be more appropriate in environments
// where memory pressure is a concern.
//
// Although a Path is technically mutable, by convention callers should not
// mutate a path once it has been built and passed to some other subsystem.
// Instead, use Copy and then mutate the copy before using it.
type Path []PathStep
// PathStep represents a single step down into a data structure, as part
// of a Path. PathStep is a closed interface, meaning that the only
// permitted implementations are those within this package.
type PathStep interface {
pathStepSigil() pathStepImpl
Apply(Value) (Value, error)
}
// embed pathImpl into a struct to declare it a PathStep implementation
type pathStepImpl struct{}
func (p pathStepImpl) pathStepSigil() pathStepImpl {
return p
}
// Index returns a new Path that is the reciever with an IndexStep appended
// to the end.
//
// This is provided as a convenient way to construct paths, but each call
// will create garbage so it should not be used where memory pressure is a
// concern.
func (p Path) Index(v Value) Path {
ret := make(Path, len(p)+1)
copy(ret, p)
ret[len(p)] = IndexStep{
Key: v,
}
return ret
}
// IndexPath is a convenience method to start a new Path with an IndexStep.
func IndexPath(v Value) Path {
return Path{}.Index(v)
}
// GetAttr returns a new Path that is the reciever with a GetAttrStep appended
// to the end.
//
// This is provided as a convenient way to construct paths, but each call
// will create garbage so it should not be used where memory pressure is a
// concern.
func (p Path) GetAttr(name string) Path {
ret := make(Path, len(p)+1)
copy(ret, p)
ret[len(p)] = GetAttrStep{
Name: name,
}
return ret
}
// Equals compares 2 Paths for exact equality.
func (p Path) Equals(other Path) bool {
if len(p) != len(other) {
return false
}
for i := range p {
pv := p[i]
switch pv := pv.(type) {
case GetAttrStep:
ov, ok := other[i].(GetAttrStep)
if !ok || pv != ov {
return false
}
case IndexStep:
ov, ok := other[i].(IndexStep)
if !ok {
return false
}
if !pv.Key.RawEquals(ov.Key) {
return false
}
default:
// Any invalid steps default to evaluating false.
return false
}
}
return true
}
// HasPrefix determines if the path p contains the provided prefix.
func (p Path) HasPrefix(prefix Path) bool {
if len(prefix) > len(p) {
return false
}
return p[:len(prefix)].Equals(prefix)
}
// GetAttrPath is a convenience method to start a new Path with a GetAttrStep.
func GetAttrPath(name string) Path {
return Path{}.GetAttr(name)
}
// Apply applies each of the steps in turn to successive values starting with
// the given value, and returns the result. If any step returns an error,
// the whole operation returns an error.
func (p Path) Apply(val Value) (Value, error) {
var err error
for i, step := range p {
val, err = step.Apply(val)
if err != nil {
return NilVal, fmt.Errorf("at step %d: %s", i, err)
}
}
return val, nil
}
// LastStep applies the given path up to the last step and then returns
// the resulting value and the final step.
//
// This is useful when dealing with assignment operations, since in that
// case the *value* of the last step is not important (and may not, in fact,
// present at all) and we care only about its location.
//
// Since LastStep applies all steps except the last, it will return errors
// for those steps in the same way as Apply does.
//
// If the path has *no* steps then the returned PathStep will be nil,
// representing that any operation should be applied directly to the
// given value.
func (p Path) LastStep(val Value) (Value, PathStep, error) {
var err error
if len(p) == 0 {
return val, nil, nil
}
journey := p[:len(p)-1]
val, err = journey.Apply(val)
if err != nil {
return NilVal, nil, err
}
return val, p[len(p)-1], nil
}
// Copy makes a shallow copy of the receiver. Often when paths are passed to
// caller code they come with the constraint that they are valid only until
// the caller returns, due to how they are constructed internally. Callers
// can use Copy to conveniently produce a copy of the value that _they_ control
// the validity of.
func (p Path) Copy() Path {
ret := make(Path, len(p))
copy(ret, p)
return ret
}
// IndexStep is a Step implementation representing applying the index operation
// to a value, which must be of either a list, map, or set type.
//
// When describing a path through a *type* rather than a concrete value,
// the Key may be an unknown value, indicating that the step applies to
// *any* key of the given type.
//
// When indexing into a set, the Key is actually the element being accessed
// itself, since in sets elements are their own identity.
type IndexStep struct {
pathStepImpl
Key Value
}
// Apply returns the value resulting from indexing the given value with
// our key value.
func (s IndexStep) Apply(val Value) (Value, error) {
if val == NilVal || val.IsNull() {
return NilVal, errors.New("cannot index a null value")
}
switch s.Key.Type() {
case Number:
if !(val.Type().IsListType() || val.Type().IsTupleType()) {
return NilVal, errors.New("not a list type")
}
case String:
if !val.Type().IsMapType() {
return NilVal, errors.New("not a map type")
}
default:
return NilVal, errors.New("key value not number or string")
}
has := val.HasIndex(s.Key)
if !has.IsKnown() {
return UnknownVal(val.Type().ElementType()), nil
}
if !has.True() {
return NilVal, errors.New("value does not have given index key")
}
return val.Index(s.Key), nil
}
func (s IndexStep) GoString() string {
return fmt.Sprintf("cty.IndexStep{Key:%#v}", s.Key)
}
// GetAttrStep is a Step implementation representing retrieving an attribute
// from a value, which must be of an object type.
type GetAttrStep struct {
pathStepImpl
Name string
}
// Apply returns the value of our named attribute from the given value, which
// must be of an object type that has a value of that name.
func (s GetAttrStep) Apply(val Value) (Value, error) {
if val == NilVal || val.IsNull() {
return NilVal, errors.New("cannot access attributes on a null value")
}
if !val.Type().IsObjectType() {
return NilVal, errors.New("not an object type")
}
if !val.Type().HasAttribute(s.Name) {
return NilVal, fmt.Errorf("object has no attribute %q", s.Name)
}
return val.GetAttr(s.Name), nil
}
func (s GetAttrStep) GoString() string {
return fmt.Sprintf("cty.GetAttrStep{Name:%q}", s.Name)
}

198
vendor/github.com/zclconf/go-cty/cty/path_set.go generated vendored Normal file
View File

@ -0,0 +1,198 @@
package cty
import (
"fmt"
"hash/crc64"
"github.com/zclconf/go-cty/cty/set"
)
// PathSet represents a set of Path objects. This can be used, for example,
// to talk about a subset of paths within a value that meet some criteria,
// without directly modifying the values at those paths.
type PathSet struct {
set set.Set
}
// NewPathSet creates and returns a PathSet, with initial contents optionally
// set by the given arguments.
func NewPathSet(paths ...Path) PathSet {
ret := PathSet{
set: set.NewSet(pathSetRules{}),
}
for _, path := range paths {
ret.Add(path)
}
return ret
}
// Add inserts a single given path into the set.
//
// Paths are immutable after construction by convention. It is particularly
// important not to mutate a path after it has been placed into a PathSet.
// If a Path is mutated while in a set, behavior is undefined.
func (s PathSet) Add(path Path) {
s.set.Add(path)
}
// AddAllSteps is like Add but it also adds all of the steps leading to
// the given path.
//
// For example, if given a path representing "foo.bar", it will add both
// "foo" and "bar".
func (s PathSet) AddAllSteps(path Path) {
for i := 1; i <= len(path); i++ {
s.Add(path[:i])
}
}
// Has returns true if the given path is in the receiving set.
func (s PathSet) Has(path Path) bool {
return s.set.Has(path)
}
// List makes and returns a slice of all of the paths in the receiving set,
// in an undefined but consistent order.
func (s PathSet) List() []Path {
if s.Empty() {
return nil
}
ret := make([]Path, 0, s.set.Length())
for it := s.set.Iterator(); it.Next(); {
ret = append(ret, it.Value().(Path))
}
return ret
}
// Remove modifies the receving set to no longer include the given path.
// If the given path was already absent, this is a no-op.
func (s PathSet) Remove(path Path) {
s.set.Remove(path)
}
// Empty returns true if the length of the receiving set is zero.
func (s PathSet) Empty() bool {
return s.set.Length() == 0
}
// Union returns a new set whose contents are the union of the receiver and
// the given other set.
func (s PathSet) Union(other PathSet) PathSet {
return PathSet{
set: s.set.Union(other.set),
}
}
// Intersection returns a new set whose contents are the intersection of the
// receiver and the given other set.
func (s PathSet) Intersection(other PathSet) PathSet {
return PathSet{
set: s.set.Intersection(other.set),
}
}
// Subtract returns a new set whose contents are those from the receiver with
// any elements of the other given set subtracted.
func (s PathSet) Subtract(other PathSet) PathSet {
return PathSet{
set: s.set.Subtract(other.set),
}
}
// SymmetricDifference returns a new set whose contents are the symmetric
// difference of the receiver and the given other set.
func (s PathSet) SymmetricDifference(other PathSet) PathSet {
return PathSet{
set: s.set.SymmetricDifference(other.set),
}
}
// Equal returns true if and only if both the receiver and the given other
// set contain exactly the same paths.
func (s PathSet) Equal(other PathSet) bool {
if s.set.Length() != other.set.Length() {
return false
}
// Now we know the lengths are the same we only need to test in one
// direction whether everything in one is in the other.
for it := s.set.Iterator(); it.Next(); {
if !other.set.Has(it.Value()) {
return false
}
}
return true
}
var crc64Table = crc64.MakeTable(crc64.ISO)
var indexStepPlaceholder = []byte("#")
// pathSetRules is an implementation of set.Rules from the set package,
// used internally within PathSet.
type pathSetRules struct {
}
func (r pathSetRules) Hash(v interface{}) int {
path := v.(Path)
hash := crc64.New(crc64Table)
for _, rawStep := range path {
switch step := rawStep.(type) {
case GetAttrStep:
// (this creates some garbage converting the string name to a
// []byte, but that's okay since cty is not designed to be
// used in tight loops under memory pressure.)
hash.Write([]byte(step.Name))
default:
// For any other step type we just append a predefined value,
// which means that e.g. all indexes into a given collection will
// hash to the same value but we assume that collections are
// small and thus this won't hurt too much.
hash.Write(indexStepPlaceholder)
}
}
// We discard half of the hash on 32-bit platforms; collisions just make
// our lookups take marginally longer, so not a big deal.
return int(hash.Sum64())
}
func (r pathSetRules) Equivalent(a, b interface{}) bool {
aPath := a.(Path)
bPath := b.(Path)
if len(aPath) != len(bPath) {
return false
}
for i := range aPath {
switch aStep := aPath[i].(type) {
case GetAttrStep:
bStep, ok := bPath[i].(GetAttrStep)
if !ok {
return false
}
if aStep.Name != bStep.Name {
return false
}
case IndexStep:
bStep, ok := bPath[i].(IndexStep)
if !ok {
return false
}
eq := aStep.Key.Equals(bStep.Key)
if !eq.IsKnown() || eq.False() {
return false
}
default:
// Should never happen, since we document PathStep as a closed type.
panic(fmt.Errorf("unsupported step type %T", aStep))
}
}
return true
}

122
vendor/github.com/zclconf/go-cty/cty/primitive_type.go generated vendored Normal file
View File

@ -0,0 +1,122 @@
package cty
import "math/big"
// primitiveType is the hidden implementation of the various primitive types
// that are exposed as variables in this package.
type primitiveType struct {
typeImplSigil
Kind primitiveTypeKind
}
type primitiveTypeKind byte
const (
primitiveTypeBool primitiveTypeKind = 'B'
primitiveTypeNumber primitiveTypeKind = 'N'
primitiveTypeString primitiveTypeKind = 'S'
)
func (t primitiveType) Equals(other Type) bool {
if otherP, ok := other.typeImpl.(primitiveType); ok {
return otherP.Kind == t.Kind
}
return false
}
func (t primitiveType) FriendlyName(mode friendlyTypeNameMode) string {
switch t.Kind {
case primitiveTypeBool:
return "bool"
case primitiveTypeNumber:
return "number"
case primitiveTypeString:
return "string"
default:
// should never happen
panic("invalid primitive type")
}
}
func (t primitiveType) GoString() string {
switch t.Kind {
case primitiveTypeBool:
return "cty.Bool"
case primitiveTypeNumber:
return "cty.Number"
case primitiveTypeString:
return "cty.String"
default:
// should never happen
panic("invalid primitive type")
}
}
// Number is the numeric type. Number values are arbitrary-precision
// decimal numbers, which can then be converted into Go's various numeric
// types only if they are in the appropriate range.
var Number Type
// String is the string type. String values are sequences of unicode codepoints
// encoded internally as UTF-8.
var String Type
// Bool is the boolean type. The two values of this type are True and False.
var Bool Type
// True is the truthy value of type Bool
var True Value
// False is the falsey value of type Bool
var False Value
// Zero is a number value representing exactly zero.
var Zero Value
// PositiveInfinity is a Number value representing positive infinity
var PositiveInfinity Value
// NegativeInfinity is a Number value representing negative infinity
var NegativeInfinity Value
func init() {
Number = Type{
primitiveType{Kind: primitiveTypeNumber},
}
String = Type{
primitiveType{Kind: primitiveTypeString},
}
Bool = Type{
primitiveType{Kind: primitiveTypeBool},
}
True = Value{
ty: Bool,
v: true,
}
False = Value{
ty: Bool,
v: false,
}
Zero = Value{
ty: Number,
v: big.NewFloat(0),
}
PositiveInfinity = Value{
ty: Number,
v: (&big.Float{}).SetInf(false),
}
NegativeInfinity = Value{
ty: Number,
v: (&big.Float{}).SetInf(true),
}
}
// IsPrimitiveType returns true if and only if the reciever is a primitive
// type, which means it's either number, string, or bool. Any two primitive
// types can be safely compared for equality using the standard == operator
// without panic, which is not a guarantee that holds for all types. Primitive
// types can therefore also be used in switch statements.
func (t Type) IsPrimitiveType() bool {
_, ok := t.typeImpl.(primitiveType)
return ok
}

76
vendor/github.com/zclconf/go-cty/cty/set/gob.go generated vendored Normal file
View File

@ -0,0 +1,76 @@
package set
import (
"bytes"
"encoding/gob"
"fmt"
)
// GobEncode is an implementation of the interface gob.GobEncoder, allowing
// sets to be included in structures encoded via gob.
//
// The set rules are included in the serialized value, so the caller must
// register its concrete rules type with gob.Register before using a
// set in a gob, and possibly also implement GobEncode/GobDecode to customize
// how any parameters are persisted.
//
// The set elements are also included, so if they are of non-primitive types
// they too must be registered with gob.
//
// If the produced gob values will persist for a long time, the caller must
// ensure compatibility of the rules implementation. In particular, if the
// definition of element equivalence changes between encoding and decoding
// then two distinct stored elements may be considered equivalent on decoding,
// causing the recovered set to have fewer elements than when it was stored.
func (s Set) GobEncode() ([]byte, error) {
gs := gobSet{
Version: 0,
Rules: s.rules,
Values: s.Values(),
}
buf := &bytes.Buffer{}
enc := gob.NewEncoder(buf)
err := enc.Encode(gs)
if err != nil {
return nil, fmt.Errorf("error encoding set.Set: %s", err)
}
return buf.Bytes(), nil
}
// GobDecode is the opposite of GobEncode. See GobEncode for information
// on the requirements for and caveats of including set values in gobs.
func (s *Set) GobDecode(buf []byte) error {
r := bytes.NewReader(buf)
dec := gob.NewDecoder(r)
var gs gobSet
err := dec.Decode(&gs)
if err != nil {
return fmt.Errorf("error decoding set.Set: %s", err)
}
if gs.Version != 0 {
return fmt.Errorf("unsupported set.Set encoding version %d; need 0", gs.Version)
}
victim := NewSetFromSlice(gs.Rules, gs.Values)
s.vals = victim.vals
s.rules = victim.rules
return nil
}
type gobSet struct {
Version int
Rules Rules
// The bucket-based representation is for efficient in-memory access, but
// for serialization it's enough to just retain the values themselves,
// which we can re-bucket using the rules (which may have changed!) when
// we re-inflate.
Values []interface{}
}
func init() {
gob.Register([]interface{}(nil))
}

15
vendor/github.com/zclconf/go-cty/cty/set/iterator.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
package set
type Iterator struct {
vals []interface{}
idx int
}
func (it *Iterator) Value() interface{} {
return it.vals[it.idx]
}
func (it *Iterator) Next() bool {
it.idx++
return it.idx < len(it.vals)
}

210
vendor/github.com/zclconf/go-cty/cty/set/ops.go generated vendored Normal file
View File

@ -0,0 +1,210 @@
package set
import (
"sort"
)
// Add inserts the given value into the receiving Set.
//
// This mutates the set in-place. This operation is not thread-safe.
func (s Set) Add(val interface{}) {
hv := s.rules.Hash(val)
if _, ok := s.vals[hv]; !ok {
s.vals[hv] = make([]interface{}, 0, 1)
}
bucket := s.vals[hv]
// See if an equivalent value is already present
for _, ev := range bucket {
if s.rules.Equivalent(val, ev) {
return
}
}
s.vals[hv] = append(bucket, val)
}
// Remove deletes the given value from the receiving set, if indeed it was
// there in the first place. If the value is not present, this is a no-op.
func (s Set) Remove(val interface{}) {
hv := s.rules.Hash(val)
bucket, ok := s.vals[hv]
if !ok {
return
}
for i, ev := range bucket {
if s.rules.Equivalent(val, ev) {
newBucket := make([]interface{}, 0, len(bucket)-1)
newBucket = append(newBucket, bucket[:i]...)
newBucket = append(newBucket, bucket[i+1:]...)
if len(newBucket) > 0 {
s.vals[hv] = newBucket
} else {
delete(s.vals, hv)
}
return
}
}
}
// Has returns true if the given value is in the receiving set, or false if
// it is not.
func (s Set) Has(val interface{}) bool {
hv := s.rules.Hash(val)
bucket, ok := s.vals[hv]
if !ok {
return false
}
for _, ev := range bucket {
if s.rules.Equivalent(val, ev) {
return true
}
}
return false
}
// Copy performs a shallow copy of the receiving set, returning a new set
// with the same rules and elements.
func (s Set) Copy() Set {
ret := NewSet(s.rules)
for k, v := range s.vals {
ret.vals[k] = v
}
return ret
}
// Iterator returns an iterator over values in the set. If the set's rules
// implement OrderedRules then the result is ordered per those rules. If
// no order is provided, or if it is not a total order, then the iteration
// order is undefined but consistent for a particular version of cty. Do not
// rely on specific ordering between cty releases unless the rules order is a
// total order.
//
// The pattern for using the returned iterator is:
//
// it := set.Iterator()
// for it.Next() {
// val := it.Value()
// // ...
// }
//
// Once an iterator has been created for a set, the set *must not* be mutated
// until the iterator is no longer in use.
func (s Set) Iterator() *Iterator {
vals := s.Values()
return &Iterator{
vals: vals,
idx: -1,
}
}
// EachValue calls the given callback once for each value in the set, in an
// undefined order that callers should not depend on.
func (s Set) EachValue(cb func(interface{})) {
it := s.Iterator()
for it.Next() {
cb(it.Value())
}
}
// Values returns a slice of all the values in the set. If the set rules have
// an order then the result is in that order. If no order is provided or if
// it is not a total order then the result order is undefined, but consistent
// for a particular set value within a specific release of cty.
func (s Set) Values() []interface{} {
var ret []interface{}
// Sort the bucketIds to ensure that we always traverse in a
// consistent order.
bucketIDs := make([]int, 0, len(s.vals))
for id := range s.vals {
bucketIDs = append(bucketIDs, id)
}
sort.Ints(bucketIDs)
for _, bucketID := range bucketIDs {
ret = append(ret, s.vals[bucketID]...)
}
if orderRules, ok := s.rules.(OrderedRules); ok {
sort.SliceStable(ret, func(i, j int) bool {
return orderRules.Less(ret[i], ret[j])
})
}
return ret
}
// Length returns the number of values in the set.
func (s Set) Length() int {
var count int
for _, bucket := range s.vals {
count = count + len(bucket)
}
return count
}
// Union returns a new set that contains all of the members of both the
// receiving set and the given set. Both sets must have the same rules, or
// else this function will panic.
func (s1 Set) Union(s2 Set) Set {
mustHaveSameRules(s1, s2)
rs := NewSet(s1.rules)
s1.EachValue(func(v interface{}) {
rs.Add(v)
})
s2.EachValue(func(v interface{}) {
rs.Add(v)
})
return rs
}
// Intersection returns a new set that contains the values that both the
// receiver and given sets have in common. Both sets must have the same rules,
// or else this function will panic.
func (s1 Set) Intersection(s2 Set) Set {
mustHaveSameRules(s1, s2)
rs := NewSet(s1.rules)
s1.EachValue(func(v interface{}) {
if s2.Has(v) {
rs.Add(v)
}
})
return rs
}
// Subtract returns a new set that contains all of the values from the receiver
// that are not also in the given set. Both sets must have the same rules,
// or else this function will panic.
func (s1 Set) Subtract(s2 Set) Set {
mustHaveSameRules(s1, s2)
rs := NewSet(s1.rules)
s1.EachValue(func(v interface{}) {
if !s2.Has(v) {
rs.Add(v)
}
})
return rs
}
// SymmetricDifference returns a new set that contains all of the values from
// both the receiver and given sets, except those that both sets have in
// common. Both sets must have the same rules, or else this function will
// panic.
func (s1 Set) SymmetricDifference(s2 Set) Set {
mustHaveSameRules(s1, s2)
rs := NewSet(s1.rules)
s1.EachValue(func(v interface{}) {
if !s2.Has(v) {
rs.Add(v)
}
})
s2.EachValue(func(v interface{}) {
if !s1.Has(v) {
rs.Add(v)
}
})
return rs
}

43
vendor/github.com/zclconf/go-cty/cty/set/rules.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
package set
// Rules represents the operations that define membership for a Set.
//
// Each Set has a Rules instance, whose methods must satisfy the interface
// contracts given below for any value that will be added to the set.
type Rules interface {
// Hash returns an int that somewhat-uniquely identifies the given value.
//
// A good hash function will minimize collisions for values that will be
// added to the set, though collisions *are* permitted. Collisions will
// simply reduce the efficiency of operations on the set.
Hash(interface{}) int
// Equivalent returns true if and only if the two values are considered
// equivalent for the sake of set membership. Two values that are
// equivalent cannot exist in the set at the same time, and if two
// equivalent values are added it is undefined which one will be
// returned when enumerating all of the set members.
//
// Two values that are equivalent *must* result in the same hash value,
// though it is *not* required that two values with the same hash value
// be equivalent.
Equivalent(interface{}, interface{}) bool
}
// OrderedRules is an extension of Rules that can apply a partial order to
// element values. When a set's Rules implements OrderedRules an iterator
// over the set will return items in the order described by the rules.
//
// If the given order is not a total order (that is, some pairs of non-equivalent
// elements do not have a defined order) then the resulting iteration order
// is undefined but consistent for a particular version of cty. The exact
// order in that case is not part of the contract and is subject to change
// between versions.
type OrderedRules interface {
Rules
// Less returns true if and only if the first argument should sort before
// the second argument. If the second argument should sort before the first
// or if there is no defined order for the values, return false.
Less(interface{}, interface{}) bool
}

62
vendor/github.com/zclconf/go-cty/cty/set/set.go generated vendored Normal file
View File

@ -0,0 +1,62 @@
package set
import (
"fmt"
)
// Set is an implementation of the concept of a set: a collection where all
// values are conceptually either in or out of the set, but the members are
// not ordered.
//
// This type primarily exists to be the internal type of sets in cty, but
// it is considered to be at the same level of abstraction as Go's built in
// slice and map collection types, and so should make no cty-specific
// assumptions.
//
// Set operations are not thread safe. It is the caller's responsibility to
// provide mutex guarantees where necessary.
//
// Set operations are not optimized to minimize memory pressure. Mutating
// a set will generally create garbage and so should perhaps be avoided in
// tight loops where memory pressure is a concern.
type Set struct {
vals map[int][]interface{}
rules Rules
}
// NewSet returns an empty set with the membership rules given.
func NewSet(rules Rules) Set {
return Set{
vals: map[int][]interface{}{},
rules: rules,
}
}
func NewSetFromSlice(rules Rules, vals []interface{}) Set {
s := NewSet(rules)
for _, v := range vals {
s.Add(v)
}
return s
}
func sameRules(s1 Set, s2 Set) bool {
return s1.rules == s2.rules
}
func mustHaveSameRules(s1 Set, s2 Set) {
if !sameRules(s1, s2) {
panic(fmt.Errorf("incompatible set rules: %#v, %#v", s1.rules, s2.rules))
}
}
// HasRules returns true if and only if the receiving set has the given rules
// instance as its rules.
func (s Set) HasRules(rules Rules) bool {
return s.rules == rules
}
// Rules returns the receiving set's rules instance.
func (s Set) Rules() Rules {
return s.rules
}

132
vendor/github.com/zclconf/go-cty/cty/set_helper.go generated vendored Normal file
View File

@ -0,0 +1,132 @@
package cty
import (
"fmt"
"github.com/zclconf/go-cty/cty/set"
)
// ValueSet is to cty.Set what []cty.Value is to cty.List and
// map[string]cty.Value is to cty.Map. It's provided to allow callers a
// convenient interface for manipulating sets before wrapping them in cty.Set
// values using cty.SetValFromValueSet.
//
// Unlike value slices and value maps, ValueSet instances have a single
// homogenous element type because that is a requirement of the underlying
// set implementation, which uses the element type to select a suitable
// hashing function.
//
// Set mutations are not concurrency-safe.
type ValueSet struct {
// ValueSet is just a thin wrapper around a set.Set with our value-oriented
// "rules" applied. We do this so that the caller can work in terms of
// cty.Value objects even though the set internals use the raw values.
s set.Set
}
// NewValueSet creates and returns a new ValueSet with the given element type.
func NewValueSet(ety Type) ValueSet {
return newValueSet(set.NewSet(setRules{Type: ety}))
}
func newValueSet(s set.Set) ValueSet {
return ValueSet{
s: s,
}
}
// ElementType returns the element type for the receiving ValueSet.
func (s ValueSet) ElementType() Type {
return s.s.Rules().(setRules).Type
}
// Add inserts the given value into the receiving set.
func (s ValueSet) Add(v Value) {
s.requireElementType(v)
s.s.Add(v.v)
}
// Remove deletes the given value from the receiving set, if indeed it was
// there in the first place. If the value is not present, this is a no-op.
func (s ValueSet) Remove(v Value) {
s.requireElementType(v)
s.s.Remove(v.v)
}
// Has returns true if the given value is in the receiving set, or false if
// it is not.
func (s ValueSet) Has(v Value) bool {
s.requireElementType(v)
return s.s.Has(v.v)
}
// Copy performs a shallow copy of the receiving set, returning a new set
// with the same rules and elements.
func (s ValueSet) Copy() ValueSet {
return newValueSet(s.s.Copy())
}
// Length returns the number of values in the set.
func (s ValueSet) Length() int {
return s.s.Length()
}
// Values returns a slice of all of the values in the set in no particular
// order.
func (s ValueSet) Values() []Value {
l := s.s.Length()
if l == 0 {
return nil
}
ret := make([]Value, 0, l)
ety := s.ElementType()
for it := s.s.Iterator(); it.Next(); {
ret = append(ret, Value{
ty: ety,
v: it.Value(),
})
}
return ret
}
// Union returns a new set that contains all of the members of both the
// receiving set and the given set. Both sets must have the same element type,
// or else this function will panic.
func (s ValueSet) Union(other ValueSet) ValueSet {
return newValueSet(s.s.Union(other.s))
}
// Intersection returns a new set that contains the values that both the
// receiver and given sets have in common. Both sets must have the same element
// type, or else this function will panic.
func (s ValueSet) Intersection(other ValueSet) ValueSet {
return newValueSet(s.s.Intersection(other.s))
}
// Subtract returns a new set that contains all of the values from the receiver
// that are not also in the given set. Both sets must have the same element
// type, or else this function will panic.
func (s ValueSet) Subtract(other ValueSet) ValueSet {
return newValueSet(s.s.Subtract(other.s))
}
// SymmetricDifference returns a new set that contains all of the values from
// both the receiver and given sets, except those that both sets have in
// common. Both sets must have the same element type, or else this function
// will panic.
func (s ValueSet) SymmetricDifference(other ValueSet) ValueSet {
return newValueSet(s.s.SymmetricDifference(other.s))
}
// requireElementType panics if the given value is not of the set's element type.
//
// It also panics if the given value is marked, because marked values cannot
// be stored in sets.
func (s ValueSet) requireElementType(v Value) {
if v.IsMarked() {
panic("cannot store marked value directly in a set (make the set itself unknown instead)")
}
if !v.Type().Equals(s.ElementType()) {
panic(fmt.Errorf("attempt to use %#v value with set of %#v", v.Type(), s.ElementType()))
}
}

244
vendor/github.com/zclconf/go-cty/cty/set_internals.go generated vendored Normal file
View File

@ -0,0 +1,244 @@
package cty
import (
"bytes"
"fmt"
"hash/crc32"
"math/big"
"sort"
"github.com/zclconf/go-cty/cty/set"
)
// setRules provides a Rules implementation for the ./set package that
// respects the equality rules for cty values of the given type.
//
// This implementation expects that values added to the set will be
// valid internal values for the given Type, which is to say that wrapping
// the given value in a Value struct along with the ruleset's type should
// produce a valid, working Value.
type setRules struct {
Type Type
}
var _ set.OrderedRules = setRules{}
// Hash returns a hash value for the receiver that can be used for equality
// checks where some inaccuracy is tolerable.
//
// The hash function is value-type-specific, so it is not meaningful to compare
// hash results for values of different types.
//
// This function is not safe to use for security-related applications, since
// the hash used is not strong enough.
func (val Value) Hash() int {
hashBytes, marks := makeSetHashBytes(val)
if len(marks) > 0 {
panic("can't take hash of value that has marks or has embedded values that have marks")
}
return int(crc32.ChecksumIEEE(hashBytes))
}
func (r setRules) Hash(v interface{}) int {
return Value{
ty: r.Type,
v: v,
}.Hash()
}
func (r setRules) Equivalent(v1 interface{}, v2 interface{}) bool {
v1v := Value{
ty: r.Type,
v: v1,
}
v2v := Value{
ty: r.Type,
v: v2,
}
eqv := v1v.Equals(v2v)
// By comparing the result to true we ensure that an Unknown result,
// which will result if either value is unknown, will be considered
// as non-equivalent. Two unknown values are not equivalent for the
// sake of set membership.
return eqv.v == true
}
// Less is an implementation of set.OrderedRules so that we can iterate over
// set elements in a consistent order, where such an order is possible.
func (r setRules) Less(v1, v2 interface{}) bool {
v1v := Value{
ty: r.Type,
v: v1,
}
v2v := Value{
ty: r.Type,
v: v2,
}
if v1v.RawEquals(v2v) { // Easy case: if they are equal then v1 can't be less
return false
}
// Null values always sort after non-null values
if v2v.IsNull() && !v1v.IsNull() {
return true
} else if v1v.IsNull() {
return false
}
// Unknown values always sort after known values
if v1v.IsKnown() && !v2v.IsKnown() {
return true
} else if !v1v.IsKnown() {
return false
}
switch r.Type {
case String:
// String values sort lexicographically
return v1v.AsString() < v2v.AsString()
case Bool:
// Weird to have a set of bools, but if we do then false sorts before true.
if v2v.True() || !v1v.True() {
return true
}
return false
case Number:
v1f := v1v.AsBigFloat()
v2f := v2v.AsBigFloat()
return v1f.Cmp(v2f) < 0
default:
// No other types have a well-defined ordering, so we just produce a
// default consistent-but-undefined ordering then. This situation is
// not considered a compatibility constraint; callers should rely only
// on the ordering rules for primitive values.
v1h, _ := makeSetHashBytes(v1v)
v2h, _ := makeSetHashBytes(v2v)
return bytes.Compare(v1h, v2h) < 0
}
}
func makeSetHashBytes(val Value) ([]byte, ValueMarks) {
var buf bytes.Buffer
marks := make(ValueMarks)
appendSetHashBytes(val, &buf, marks)
return buf.Bytes(), marks
}
func appendSetHashBytes(val Value, buf *bytes.Buffer, marks ValueMarks) {
// Exactly what bytes we generate here don't matter as long as the following
// constraints hold:
// - Unknown and null values all generate distinct strings from
// each other and from any normal value of the given type.
// - The delimiter used to separate items in a compound structure can
// never appear literally in any of its elements.
// Since we don't support hetrogenous lists we don't need to worry about
// collisions between values of different types, apart from
// PseudoTypeDynamic.
// If in practice we *do* get a collision then it's not a big deal because
// the Equivalent function will still distinguish values, but set
// performance will be best if we are able to produce a distinct string
// for each distinct value, unknown values notwithstanding.
// Marks aren't considered part of a value for equality-testing purposes,
// so we'll unmark our value before we work with it but we'll remember
// the marks in case the caller needs to re-apply them to a derived
// value.
if val.IsMarked() {
unmarkedVal, valMarks := val.Unmark()
for m := range valMarks {
marks[m] = struct{}{}
}
val = unmarkedVal
}
if !val.IsKnown() {
buf.WriteRune('?')
return
}
if val.IsNull() {
buf.WriteRune('~')
return
}
switch val.ty {
case Number:
// Due to an unfortunate quirk of gob encoding for big.Float, we end up
// with non-pointer values immediately after a gob round-trip, and
// we end up in here before we've had a chance to run
// gobDecodeFixNumberPtr on the inner values of a gob-encoded set,
// and so sadly we must make a special effort to handle that situation
// here just so that we can get far enough along to fix it up for
// everything else in this package.
if bf, ok := val.v.(big.Float); ok {
buf.WriteString(bf.String())
return
}
buf.WriteString(val.v.(*big.Float).String())
return
case Bool:
if val.v.(bool) {
buf.WriteRune('T')
} else {
buf.WriteRune('F')
}
return
case String:
buf.WriteString(fmt.Sprintf("%q", val.v.(string)))
return
}
if val.ty.IsMapType() {
buf.WriteRune('{')
val.ForEachElement(func(keyVal, elementVal Value) bool {
appendSetHashBytes(keyVal, buf, marks)
buf.WriteRune(':')
appendSetHashBytes(elementVal, buf, marks)
buf.WriteRune(';')
return false
})
buf.WriteRune('}')
return
}
if val.ty.IsListType() || val.ty.IsSetType() {
buf.WriteRune('[')
val.ForEachElement(func(keyVal, elementVal Value) bool {
appendSetHashBytes(elementVal, buf, marks)
buf.WriteRune(';')
return false
})
buf.WriteRune(']')
return
}
if val.ty.IsObjectType() {
buf.WriteRune('<')
attrNames := make([]string, 0, len(val.ty.AttributeTypes()))
for attrName := range val.ty.AttributeTypes() {
attrNames = append(attrNames, attrName)
}
sort.Strings(attrNames)
for _, attrName := range attrNames {
appendSetHashBytes(val.GetAttr(attrName), buf, marks)
buf.WriteRune(';')
}
buf.WriteRune('>')
return
}
if val.ty.IsTupleType() {
buf.WriteRune('<')
val.ForEachElement(func(keyVal, elementVal Value) bool {
appendSetHashBytes(elementVal, buf, marks)
buf.WriteRune(';')
return false
})
buf.WriteRune('>')
return
}
// should never get down here
panic("unsupported type in set hash")
}

72
vendor/github.com/zclconf/go-cty/cty/set_type.go generated vendored Normal file
View File

@ -0,0 +1,72 @@
package cty
import (
"fmt"
)
type typeSet struct {
typeImplSigil
ElementTypeT Type
}
// Set creates a set type with the given element Type.
//
// Set types are CollectionType implementations.
func Set(elem Type) Type {
return Type{
typeSet{
ElementTypeT: elem,
},
}
}
// Equals returns true if the other Type is a set whose element type is
// equal to that of the receiver.
func (t typeSet) Equals(other Type) bool {
ot, isSet := other.typeImpl.(typeSet)
if !isSet {
return false
}
return t.ElementTypeT.Equals(ot.ElementTypeT)
}
func (t typeSet) FriendlyName(mode friendlyTypeNameMode) string {
elemName := t.ElementTypeT.friendlyNameMode(mode)
if mode == friendlyTypeConstraintName {
if t.ElementTypeT == DynamicPseudoType {
elemName = "any single type"
}
}
return "set of " + elemName
}
func (t typeSet) ElementType() Type {
return t.ElementTypeT
}
func (t typeSet) GoString() string {
return fmt.Sprintf("cty.Set(%#v)", t.ElementTypeT)
}
// IsSetType returns true if the given type is a list type, regardless of its
// element type.
func (t Type) IsSetType() bool {
_, ok := t.typeImpl.(typeSet)
return ok
}
// SetElementType is a convenience method that checks if the given type is
// a set type, returning a pointer to its element type if so and nil
// otherwise. This is intended to allow convenient conditional branches,
// like so:
//
// if et := t.SetElementType(); et != nil {
// // Do something with *et
// }
func (t Type) SetElementType() *Type {
if lt, ok := t.typeImpl.(typeSet); ok {
return &lt.ElementTypeT
}
return nil
}

121
vendor/github.com/zclconf/go-cty/cty/tuple_type.go generated vendored Normal file
View File

@ -0,0 +1,121 @@
package cty
import (
"fmt"
)
type typeTuple struct {
typeImplSigil
ElemTypes []Type
}
// Tuple creates a tuple type with the given element types.
//
// After a slice is passed to this function the caller must no longer access
// the underlying array, since ownership is transferred to this library.
func Tuple(elemTypes []Type) Type {
return Type{
typeTuple{
ElemTypes: elemTypes,
},
}
}
func (t typeTuple) Equals(other Type) bool {
if ot, ok := other.typeImpl.(typeTuple); ok {
if len(t.ElemTypes) != len(ot.ElemTypes) {
// Fast path: if we don't have the same number of elements
// then we can't possibly be equal.
return false
}
for i, ty := range t.ElemTypes {
oty := ot.ElemTypes[i]
if !ok {
return false
}
if !oty.Equals(ty) {
return false
}
}
return true
}
return false
}
func (t typeTuple) FriendlyName(mode friendlyTypeNameMode) string {
// There isn't really a friendly way to write a tuple type due to its
// complexity, so we'll just do something English-ish. Callers will
// probably want to make some extra effort to avoid ever printing out
// a tuple type FriendlyName in its entirety. For example, could
// produce an error message by diffing two object types and saying
// something like "Expected attribute foo to be string, but got number".
// TODO: Finish this
return "tuple"
}
func (t typeTuple) GoString() string {
if len(t.ElemTypes) == 0 {
return "cty.EmptyTuple"
}
return fmt.Sprintf("cty.Tuple(%#v)", t.ElemTypes)
}
// EmptyTuple is a shorthand for Tuple([]Type{}), to more easily talk about
// the empty tuple type.
var EmptyTuple Type
// EmptyTupleVal is the only possible non-null, non-unknown value of type
// EmptyTuple.
var EmptyTupleVal Value
func init() {
EmptyTuple = Tuple([]Type{})
EmptyTupleVal = Value{
ty: EmptyTuple,
v: []interface{}{},
}
}
// IsTupleType returns true if the given type is an object type, regardless
// of its element type.
func (t Type) IsTupleType() bool {
_, ok := t.typeImpl.(typeTuple)
return ok
}
// Length returns the number of elements of the receiving tuple type.
// Will panic if the reciever isn't a tuple type; use IsTupleType to determine
// whether this operation will succeed.
func (t Type) Length() int {
if ot, ok := t.typeImpl.(typeTuple); ok {
return len(ot.ElemTypes)
}
panic("Length on non-tuple Type")
}
// TupleElementType returns the type of the element with the given index. Will
// panic if the receiver is not a tuple type (use IsTupleType to confirm)
// or if the index is out of range (use Length to confirm).
func (t Type) TupleElementType(idx int) Type {
if ot, ok := t.typeImpl.(typeTuple); ok {
return ot.ElemTypes[idx]
}
panic("TupleElementType on non-tuple Type")
}
// TupleElementTypes returns a slice of the recieving tuple type's element
// types. Will panic if the receiver is not a tuple type (use IsTupleType
// to confirm).
//
// The returned slice is part of the internal state of the type, and is provided
// for read access only. It is forbidden for any caller to modify the
// underlying array. For many purposes the element-related methods of Value
// are more appropriate and more convenient to use.
func (t Type) TupleElementTypes() []Type {
if ot, ok := t.typeImpl.(typeTuple); ok {
return ot.ElemTypes
}
panic("TupleElementTypes on non-tuple Type")
}

120
vendor/github.com/zclconf/go-cty/cty/type.go generated vendored Normal file
View File

@ -0,0 +1,120 @@
package cty
// Type represents value types within the type system.
//
// This is a closed interface type, meaning that only the concrete
// implementations provided within this package are considered valid.
type Type struct {
typeImpl
}
type typeImpl interface {
// isTypeImpl is a do-nothing method that exists only to express
// that a type is an implementation of typeImpl.
isTypeImpl() typeImplSigil
// Equals returns true if the other given Type exactly equals the
// receiver Type.
Equals(other Type) bool
// FriendlyName returns a human-friendly *English* name for the given
// type.
FriendlyName(mode friendlyTypeNameMode) string
// GoString implements the GoStringer interface from package fmt.
GoString() string
}
// Base implementation of Type to embed into concrete implementations
// to signal that they are implementations of Type.
type typeImplSigil struct{}
func (t typeImplSigil) isTypeImpl() typeImplSigil {
return typeImplSigil{}
}
// Equals returns true if the other given Type exactly equals the receiver
// type.
func (t Type) Equals(other Type) bool {
return t.typeImpl.Equals(other)
}
// FriendlyName returns a human-friendly *English* name for the given type.
func (t Type) FriendlyName() string {
return t.typeImpl.FriendlyName(friendlyTypeName)
}
// FriendlyNameForConstraint is similar to FriendlyName except that the
// result is specialized for describing type _constraints_ rather than types
// themselves. This is more appropriate when reporting that a particular value
// does not conform to an expected type constraint.
//
// In particular, this function uses the term "any type" to refer to
// cty.DynamicPseudoType, rather than "dynamic" as returned by FriendlyName.
func (t Type) FriendlyNameForConstraint() string {
return t.typeImpl.FriendlyName(friendlyTypeConstraintName)
}
// friendlyNameMode is an internal combination of the various FriendlyName*
// variants that just directly takes a mode, for easy passthrough for
// recursive name construction.
func (t Type) friendlyNameMode(mode friendlyTypeNameMode) string {
return t.typeImpl.FriendlyName(mode)
}
// GoString returns a string approximating how the receiver type would be
// expressed in Go source code.
func (t Type) GoString() string {
if t.typeImpl == nil {
return "cty.NilType"
}
return t.typeImpl.GoString()
}
// NilType is an invalid type used when a function is returning an error
// and has no useful type to return. It should not be used and any methods
// called on it will panic.
var NilType = Type{}
// HasDynamicTypes returns true either if the receiver is itself
// DynamicPseudoType or if it is a compound type whose descendent elements
// are DynamicPseudoType.
func (t Type) HasDynamicTypes() bool {
switch {
case t == DynamicPseudoType:
return true
case t.IsPrimitiveType():
return false
case t.IsCollectionType():
return false
case t.IsObjectType():
attrTypes := t.AttributeTypes()
for _, at := range attrTypes {
if at.HasDynamicTypes() {
return true
}
}
return false
case t.IsTupleType():
elemTypes := t.TupleElementTypes()
for _, et := range elemTypes {
if et.HasDynamicTypes() {
return true
}
}
return false
case t.IsCapsuleType():
return false
default:
// Should never happen, since above should be exhaustive
panic("HasDynamicTypes does not support the given type")
}
}
type friendlyTypeNameMode rune
const (
friendlyTypeName friendlyTypeNameMode = 'N'
friendlyTypeConstraintName friendlyTypeNameMode = 'C'
)

139
vendor/github.com/zclconf/go-cty/cty/type_conform.go generated vendored Normal file
View File

@ -0,0 +1,139 @@
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()
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()),
)
}

View File

@ -0,0 +1,57 @@
package cty
import (
"encoding/gob"
"fmt"
"math/big"
"strings"
"github.com/zclconf/go-cty/cty/set"
)
// InternalTypesToRegister is a slice of values that covers all of the
// internal types used in the representation of cty.Type and cty.Value
// across all cty Types.
//
// This is intended to be used to register these types with encoding
// packages that require registration of types used in interfaces, such as
// encoding/gob, thus allowing cty types and values to be included in streams
// created from those packages. However, registering with gob is not necessary
// since that is done automatically as a side-effect of importing this package.
//
// Callers should not do anything with the values here except pass them on
// verbatim to a registration function.
//
// If the calling application uses Capsule types that wrap local structs either
// directly or indirectly, these structs may also need to be registered in
// order to support encoding and decoding of values of these types. That is the
// responsibility of the calling application.
var InternalTypesToRegister []interface{}
func init() {
InternalTypesToRegister = []interface{}{
primitiveType{},
typeList{},
typeMap{},
typeObject{},
typeSet{},
setRules{},
set.Set{},
typeTuple{},
big.Float{},
capsuleType{},
[]interface{}(nil),
map[string]interface{}(nil),
}
// Register these with gob here, rather than in gob.go, to ensure
// that this will always happen after we build the above.
for _, tv := range InternalTypesToRegister {
typeName := fmt.Sprintf("%T", tv)
if strings.HasPrefix(typeName, "cty.") {
gob.RegisterName(fmt.Sprintf("github.com/zclconf/go-cty/%s", typeName), tv)
} else {
gob.Register(tv)
}
}
}

84
vendor/github.com/zclconf/go-cty/cty/unknown.go generated vendored Normal file
View File

@ -0,0 +1,84 @@
package cty
// unknownType is the placeholder type used for the sigil value representing
// "Unknown", to make it unambigiously distinct from any other possible value.
type unknownType struct {
}
// Unknown is a special value that can be
var unknown interface{} = &unknownType{}
// UnknownVal returns an Value that represents an unknown value of the given
// type. Unknown values can be used to represent a value that is
// not yet known. Its meaning is undefined in cty, but it could be used by
// an calling application to allow partial evaluation.
//
// Unknown values of any type can be created of any type. All operations on
// Unknown values themselves return Unknown.
func UnknownVal(t Type) Value {
return Value{
ty: t,
v: unknown,
}
}
func (t unknownType) GoString() string {
// This is the stringification of our internal unknown marker. The
// stringification of the public representation of unknowns is in
// Value.GoString.
return "cty.unknown"
}
type pseudoTypeDynamic struct {
typeImplSigil
}
// DynamicPseudoType represents the dynamic pseudo-type.
//
// This type can represent situations where a type is not yet known. Its
// meaning is undefined in cty, but it could be used by a calling
// application to allow expression type checking with some types not yet known.
// For example, the application might optimistically permit any operation on
// values of this type in type checking, allowing a partial type-check result,
// and then repeat the check when more information is known to get the
// final, concrete type.
//
// It is a pseudo-type because it is used only as a sigil to the calling
// application. "Unknown" is the only valid value of this pseudo-type, so
// operations on values of this type will always short-circuit as per
// the rules for that special value.
var DynamicPseudoType Type
func (t pseudoTypeDynamic) Equals(other Type) bool {
_, ok := other.typeImpl.(pseudoTypeDynamic)
return ok
}
func (t pseudoTypeDynamic) FriendlyName(mode friendlyTypeNameMode) string {
switch mode {
case friendlyTypeConstraintName:
return "any type"
default:
return "dynamic"
}
}
func (t pseudoTypeDynamic) GoString() string {
return "cty.DynamicPseudoType"
}
// DynamicVal is the only valid value of the pseudo-type dynamic.
// This value can be used as a placeholder where a value or expression's
// type and value are both unknown, thus allowing partial evaluation. See
// the docs for DynamicPseudoType for more information.
var DynamicVal Value
func init() {
DynamicPseudoType = Type{
pseudoTypeDynamic{},
}
DynamicVal = Value{
ty: DynamicPseudoType,
v: unknown,
}
}

View File

@ -0,0 +1,64 @@
package cty
// UnknownAsNull returns a value of the same type as the given value but
// with any unknown values (including nested values) replaced with null
// values of the same type.
//
// This can be useful if a result is to be serialized in a format that can't
// represent unknowns, such as JSON, as long as the caller does not need to
// retain the unknown value information.
func UnknownAsNull(val Value) Value {
ty := val.Type()
switch {
case val.IsNull():
return val
case !val.IsKnown():
return NullVal(ty)
case ty.IsListType() || ty.IsTupleType() || ty.IsSetType():
length := val.LengthInt()
if length == 0 {
// If there are no elements then we can't have unknowns
return val
}
vals := make([]Value, 0, length)
it := val.ElementIterator()
for it.Next() {
_, v := it.Element()
vals = append(vals, UnknownAsNull(v))
}
switch {
case ty.IsListType():
return ListVal(vals)
case ty.IsTupleType():
return TupleVal(vals)
default:
return SetVal(vals)
}
case ty.IsMapType() || ty.IsObjectType():
var length int
switch {
case ty.IsMapType():
length = val.LengthInt()
default:
length = len(val.Type().AttributeTypes())
}
if length == 0 {
// If there are no elements then we can't have unknowns
return val
}
vals := make(map[string]Value, length)
it := val.ElementIterator()
for it.Next() {
k, v := it.Element()
vals[k.AsString()] = UnknownAsNull(v)
}
switch {
case ty.IsMapType():
return MapVal(vals)
default:
return ObjectVal(vals)
}
}
return val
}

108
vendor/github.com/zclconf/go-cty/cty/value.go generated vendored Normal file
View File

@ -0,0 +1,108 @@
package cty
// Value represents a value of a particular type, and is the interface by
// which operations are executed on typed values.
//
// Value has two different classes of method. Operation methods stay entirely
// within the type system (methods accept and return Value instances) and
// are intended for use in implementing a language in terms of cty, while
// integration methods either enter or leave the type system, working with
// native Go values. Operation methods are guaranteed to support all of the
// expected short-circuit behavior for unknown and dynamic values, while
// integration methods may not.
//
// The philosophy for the operations API is that it's the caller's
// responsibility to ensure that the given types and values satisfy the
// specified invariants during a separate type check, so that the caller is
// able to return errors to its user from the application's own perspective.
//
// Consequently the design of these methods assumes such checks have already
// been done and panics if any invariants turn out not to be satisfied. These
// panic errors are not intended to be handled, but rather indicate a bug in
// the calling application that should be fixed with more checks prior to
// executing operations.
//
// A related consequence of this philosophy is that no automatic type
// conversions are done. If a method specifies that its argument must be
// number then it's the caller's responsibility to do that conversion before
// the call, thus allowing the application to have more constrained conversion
// rules than are offered by the built-in converter where necessary.
type Value struct {
ty Type
v interface{}
}
// Type returns the type of the value.
func (val Value) Type() Type {
return val.ty
}
// IsKnown returns true if the value is known. That is, if it is not
// the result of the unknown value constructor Unknown(...), and is not
// the result of an operation on another unknown value.
//
// Unknown values are only produced either directly or as a result of
// operating on other unknown values, and so an application that never
// introduces Unknown values can be guaranteed to never receive any either.
func (val Value) IsKnown() bool {
if val.IsMarked() {
return val.unmarkForce().IsKnown()
}
return val.v != unknown
}
// IsNull returns true if the value is null. Values of any type can be
// null, but any operations on a null value will panic. No operation ever
// produces null, so an application that never introduces Null values can
// be guaranteed to never receive any either.
func (val Value) IsNull() bool {
if val.IsMarked() {
return val.unmarkForce().IsNull()
}
return val.v == nil
}
// NilVal is an invalid Value that can be used as a placeholder when returning
// with an error from a function that returns (Value, error).
//
// NilVal is *not* a valid error and so no operations may be performed on it.
// Any attempt to use it will result in a panic.
//
// This should not be confused with the idea of a Null value, as returned by
// NullVal. NilVal is a nil within the *Go* type system, and is invalid in
// the cty type system. Null values *do* exist in the cty type system.
var NilVal = Value{
ty: Type{typeImpl: nil},
v: nil,
}
// IsWhollyKnown is an extension of IsKnown that also recursively checks
// inside collections and structures to see if there are any nested unknown
// values.
func (val Value) IsWhollyKnown() bool {
if val.IsMarked() {
return val.unmarkForce().IsWhollyKnown()
}
if !val.IsKnown() {
return false
}
if val.IsNull() {
// Can't recurse into a null, so we're done
return true
}
switch {
case val.CanIterateElements():
for it := val.ElementIterator(); it.Next(); {
_, ev := it.Element()
if !ev.IsWhollyKnown() {
return false
}
}
return true
default:
return true
}
}

324
vendor/github.com/zclconf/go-cty/cty/value_init.go generated vendored Normal file
View File

@ -0,0 +1,324 @@
package cty
import (
"fmt"
"math/big"
"reflect"
"golang.org/x/text/unicode/norm"
"github.com/zclconf/go-cty/cty/set"
)
// BoolVal returns a Value of type Number whose internal value is the given
// bool.
func BoolVal(v bool) Value {
return Value{
ty: Bool,
v: v,
}
}
// NumberVal returns a Value of type Number whose internal value is the given
// big.Float. The returned value becomes the owner of the big.Float object,
// and so it's forbidden for the caller to mutate the object after it's
// wrapped in this way.
func NumberVal(v *big.Float) Value {
return Value{
ty: Number,
v: v,
}
}
// ParseNumberVal returns a Value of type number produced by parsing the given
// string as a decimal real number. To ensure that two identical strings will
// always produce an equal number, always use this function to derive a number
// from a string; it will ensure that the precision and rounding mode for the
// internal big decimal is configured in a consistent way.
//
// If the given string cannot be parsed as a number, the returned error has
// the message "a number is required", making it suitable to return to an
// end-user to signal a type conversion error.
//
// If the given string contains a number that becomes a recurring fraction
// when expressed in binary then it will be truncated to have a 512-bit
// mantissa. Note that this is a higher precision than that of a float64,
// so coverting the same decimal number first to float64 and then calling
// NumberFloatVal will not produce an equal result; the conversion first
// to float64 will round the mantissa to fewer than 512 bits.
func ParseNumberVal(s string) (Value, error) {
// Base 10, precision 512, and rounding to nearest even is the standard
// way to handle numbers arriving as strings.
f, _, err := big.ParseFloat(s, 10, 512, big.ToNearestEven)
if err != nil {
return NilVal, fmt.Errorf("a number is required")
}
return NumberVal(f), nil
}
// MustParseNumberVal is like ParseNumberVal but it will panic in case of any
// error. It can be used during initialization or any other situation where
// the given string is a constant or otherwise known to be correct by the
// caller.
func MustParseNumberVal(s string) Value {
ret, err := ParseNumberVal(s)
if err != nil {
panic(err)
}
return ret
}
// NumberIntVal returns a Value of type Number whose internal value is equal
// to the given integer.
func NumberIntVal(v int64) Value {
return NumberVal(new(big.Float).SetInt64(v))
}
// NumberUIntVal returns a Value of type Number whose internal value is equal
// to the given unsigned integer.
func NumberUIntVal(v uint64) Value {
return NumberVal(new(big.Float).SetUint64(v))
}
// NumberFloatVal returns a Value of type Number whose internal value is
// equal to the given float.
func NumberFloatVal(v float64) Value {
return NumberVal(new(big.Float).SetFloat64(v))
}
// StringVal returns a Value of type String whose internal value is the
// given string.
//
// Strings must be UTF-8 encoded sequences of valid unicode codepoints, and
// they are NFC-normalized on entry into the world of cty values.
//
// If the given string is not valid UTF-8 then behavior of string operations
// is undefined.
func StringVal(v string) Value {
return Value{
ty: String,
v: NormalizeString(v),
}
}
// NormalizeString applies the same normalization that cty applies when
// constructing string values.
//
// A return value from this function can be meaningfully compared byte-for-byte
// with a Value.AsString result.
func NormalizeString(s string) string {
return norm.NFC.String(s)
}
// ObjectVal returns a Value of an object type whose structure is defined
// by the key names and value types in the given map.
func ObjectVal(attrs map[string]Value) Value {
attrTypes := make(map[string]Type, len(attrs))
attrVals := make(map[string]interface{}, len(attrs))
for attr, val := range attrs {
attr = NormalizeString(attr)
attrTypes[attr] = val.ty
attrVals[attr] = val.v
}
return Value{
ty: Object(attrTypes),
v: attrVals,
}
}
// TupleVal returns a Value of a tuple type whose element types are
// defined by the value types in the given slice.
func TupleVal(elems []Value) Value {
elemTypes := make([]Type, len(elems))
elemVals := make([]interface{}, len(elems))
for i, val := range elems {
elemTypes[i] = val.ty
elemVals[i] = val.v
}
return Value{
ty: Tuple(elemTypes),
v: elemVals,
}
}
// ListVal returns a Value of list type whose element type is defined by
// the types of the given values, which must be homogenous.
//
// If the types are not all consistent (aside from elements that are of the
// dynamic pseudo-type) then this function will panic. It will panic also
// if the given list is empty, since then the element type cannot be inferred.
// (See also ListValEmpty.)
func ListVal(vals []Value) Value {
if len(vals) == 0 {
panic("must not call ListVal with empty slice")
}
elementType := DynamicPseudoType
rawList := make([]interface{}, len(vals))
for i, val := range vals {
if elementType == DynamicPseudoType {
elementType = val.ty
} else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
panic(fmt.Errorf(
"inconsistent list element types (%#v then %#v)",
elementType, val.ty,
))
}
rawList[i] = val.v
}
return Value{
ty: List(elementType),
v: rawList,
}
}
// ListValEmpty returns an empty list of the given element type.
func ListValEmpty(element Type) Value {
return Value{
ty: List(element),
v: []interface{}{},
}
}
// MapVal returns a Value of a map type whose element type is defined by
// the types of the given values, which must be homogenous.
//
// If the types are not all consistent (aside from elements that are of the
// dynamic pseudo-type) then this function will panic. It will panic also
// if the given map is empty, since then the element type cannot be inferred.
// (See also MapValEmpty.)
func MapVal(vals map[string]Value) Value {
if len(vals) == 0 {
panic("must not call MapVal with empty map")
}
elementType := DynamicPseudoType
rawMap := make(map[string]interface{}, len(vals))
for key, val := range vals {
if elementType == DynamicPseudoType {
elementType = val.ty
} else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
panic(fmt.Errorf(
"inconsistent map element types (%#v then %#v)",
elementType, val.ty,
))
}
rawMap[NormalizeString(key)] = val.v
}
return Value{
ty: Map(elementType),
v: rawMap,
}
}
// MapValEmpty returns an empty map of the given element type.
func MapValEmpty(element Type) Value {
return Value{
ty: Map(element),
v: map[string]interface{}{},
}
}
// SetVal returns a Value of set type whose element type is defined by
// the types of the given values, which must be homogenous.
//
// If the types are not all consistent (aside from elements that are of the
// dynamic pseudo-type) then this function will panic. It will panic also
// if the given list is empty, since then the element type cannot be inferred.
// (See also SetValEmpty.)
func SetVal(vals []Value) Value {
if len(vals) == 0 {
panic("must not call SetVal with empty slice")
}
elementType := DynamicPseudoType
rawList := make([]interface{}, len(vals))
var markSets []ValueMarks
for i, val := range vals {
if unmarkedVal, marks := val.UnmarkDeep(); len(marks) > 0 {
val = unmarkedVal
markSets = append(markSets, marks)
}
if val.ContainsMarked() {
// FIXME: Allow this, but unmark the values and apply the
// marking to the set itself instead.
panic("set cannot contain marked values")
}
if elementType == DynamicPseudoType {
elementType = val.ty
} else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
panic(fmt.Errorf(
"inconsistent set element types (%#v then %#v)",
elementType, val.ty,
))
}
rawList[i] = val.v
}
rawVal := set.NewSetFromSlice(setRules{elementType}, rawList)
return Value{
ty: Set(elementType),
v: rawVal,
}.WithMarks(markSets...)
}
// SetValFromValueSet returns a Value of set type based on an already-constructed
// ValueSet.
//
// The element type of the returned value is the element type of the given
// set.
func SetValFromValueSet(s ValueSet) Value {
ety := s.ElementType()
rawVal := s.s.Copy() // copy so caller can't mutate what we wrap
return Value{
ty: Set(ety),
v: rawVal,
}
}
// SetValEmpty returns an empty set of the given element type.
func SetValEmpty(element Type) Value {
return Value{
ty: Set(element),
v: set.NewSet(setRules{element}),
}
}
// CapsuleVal creates a value of the given capsule type using the given
// wrapVal, which must be a pointer to a value of the capsule type's native
// type.
//
// This function will panic if the given type is not a capsule type, if
// the given wrapVal is not compatible with the given capsule type, or if
// wrapVal is not a pointer.
func CapsuleVal(ty Type, wrapVal interface{}) Value {
if !ty.IsCapsuleType() {
panic("not a capsule type")
}
wv := reflect.ValueOf(wrapVal)
if wv.Kind() != reflect.Ptr {
panic("wrapVal is not a pointer")
}
it := ty.typeImpl.(*capsuleType).GoType
if !wv.Type().Elem().AssignableTo(it) {
panic("wrapVal target is not compatible with the given capsule type")
}
return Value{
ty: ty,
v: wrapVal,
}
}

1290
vendor/github.com/zclconf/go-cty/cty/value_ops.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

182
vendor/github.com/zclconf/go-cty/cty/walk.go generated vendored Normal file
View File

@ -0,0 +1,182 @@
package cty
// Walk visits all of the values in a possibly-complex structure, calling
// a given function for each value.
//
// For example, given a list of strings the callback would first be called
// with the whole list and then called once for each element of the list.
//
// The callback function may prevent recursive visits to child values by
// returning false. The callback function my halt the walk altogether by
// returning a non-nil error. If the returned error is about the element
// currently being visited, it is recommended to use the provided path
// value to produce a PathError describing that context.
//
// The path passed to the given function may not be used after that function
// returns, since its backing array is re-used for other calls.
func Walk(val Value, cb func(Path, Value) (bool, error)) error {
var path Path
return walk(path, val, cb)
}
func walk(path Path, val Value, cb func(Path, Value) (bool, error)) error {
deeper, err := cb(path, val)
if err != nil {
return err
}
if !deeper {
return nil
}
if val.IsNull() || !val.IsKnown() {
// Can't recurse into null or unknown values, regardless of type
return nil
}
ty := val.Type()
switch {
case ty.IsObjectType():
for it := val.ElementIterator(); it.Next(); {
nameVal, av := it.Element()
path := append(path, GetAttrStep{
Name: nameVal.AsString(),
})
err := walk(path, av, cb)
if err != nil {
return err
}
}
case val.CanIterateElements():
for it := val.ElementIterator(); it.Next(); {
kv, ev := it.Element()
path := append(path, IndexStep{
Key: kv,
})
err := walk(path, ev, cb)
if err != nil {
return err
}
}
}
return nil
}
// Transform visits all of the values in a possibly-complex structure,
// calling a given function for each value which has an opportunity to
// replace that value.
//
// Unlike Walk, Transform visits child nodes first, so for a list of strings
// it would first visit the strings and then the _new_ list constructed
// from the transformed values of the list items.
//
// This is useful for creating the effect of being able to make deep mutations
// to a value even though values are immutable. However, it's the responsibility
// of the given function to preserve expected invariants, such as homogenity of
// element types in collections; this function can panic if such invariants
// are violated, just as if new values were constructed directly using the
// value constructor functions. An easy way to preserve invariants is to
// ensure that the transform function never changes the value type.
//
// The callback function my halt the walk altogether by
// returning a non-nil error. If the returned error is about the element
// currently being visited, it is recommended to use the provided path
// value to produce a PathError describing that context.
//
// The path passed to the given function may not be used after that function
// returns, since its backing array is re-used for other calls.
func Transform(val Value, cb func(Path, Value) (Value, error)) (Value, error) {
var path Path
return transform(path, val, cb)
}
func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value, error) {
ty := val.Type()
var newVal Value
switch {
case val.IsNull() || !val.IsKnown():
// Can't recurse into null or unknown values, regardless of type
newVal = val
case ty.IsListType() || ty.IsSetType() || ty.IsTupleType():
l := val.LengthInt()
switch l {
case 0:
// No deep transform for an empty sequence
newVal = val
default:
elems := make([]Value, 0, l)
for it := val.ElementIterator(); it.Next(); {
kv, ev := it.Element()
path := append(path, IndexStep{
Key: kv,
})
newEv, err := transform(path, ev, cb)
if err != nil {
return DynamicVal, err
}
elems = append(elems, newEv)
}
switch {
case ty.IsListType():
newVal = ListVal(elems)
case ty.IsSetType():
newVal = SetVal(elems)
case ty.IsTupleType():
newVal = TupleVal(elems)
default:
panic("unknown sequence type") // should never happen because of the case we are in
}
}
case ty.IsMapType():
l := val.LengthInt()
switch l {
case 0:
// No deep transform for an empty map
newVal = val
default:
elems := make(map[string]Value)
for it := val.ElementIterator(); it.Next(); {
kv, ev := it.Element()
path := append(path, IndexStep{
Key: kv,
})
newEv, err := transform(path, ev, cb)
if err != nil {
return DynamicVal, err
}
elems[kv.AsString()] = newEv
}
newVal = MapVal(elems)
}
case ty.IsObjectType():
switch {
case ty.Equals(EmptyObject):
// No deep transform for an empty object
newVal = val
default:
atys := ty.AttributeTypes()
newAVs := make(map[string]Value)
for name := range atys {
av := val.GetAttr(name)
path := append(path, GetAttrStep{
Name: name,
})
newAV, err := transform(path, av, cb)
if err != nil {
return DynamicVal, err
}
newAVs[name] = newAV
}
newVal = ObjectVal(newAVs)
}
default:
newVal = val
}
return cb(path, newVal)
}