updated GHA
Some checks reported errors
continuous-integration/drone/pr Build encountered an error
continuous-integration/drone/push Build encountered an error

Update to v2 SDK
updated dependencies
This commit is contained in:
2022-08-06 16:21:18 +02:00
parent 989e7079a5
commit e1266ebf64
1909 changed files with 122367 additions and 279095 deletions

21
vendor/github.com/hashicorp/go-cty/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-2018 Martin Atkins
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

128
vendor/github.com/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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,190 @@
package convert
import (
"github.com/hashicorp/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 out.IsObjectType() && in.IsMapType():
if !unsafe {
// Converting a map to an object is an "unsafe" conversion,
// because we don't know if all the map keys will correspond to
// object attributes.
return nil
}
return conversionMapToObject(in, out, 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/hashicorp/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,488 @@
package convert
import (
"github.com/hashicorp/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)
elemPath := append(path.Copy(), nil)
it := val.ElementIterator()
for it.Next() {
_, val := it.Element()
var err error
elemPath[len(elemPath)-1] = cty.IndexStep{
Key: cty.NumberIntVal(i),
}
if conv != nil {
val, err = conv(val, elemPath)
if err != nil {
return cty.NilVal, err
}
}
elems = append(elems, val)
i++
}
if len(elems) == 0 {
if ety == cty.DynamicPseudoType {
ety = val.Type().ElementType()
}
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)
elemPath := append(path.Copy(), nil)
it := val.ElementIterator()
for it.Next() {
_, val := it.Element()
var err error
elemPath[len(elemPath)-1] = cty.IndexStep{
Key: cty.NumberIntVal(i),
}
if conv != nil {
val, err = conv(val, elemPath)
if err != nil {
return cty.NilVal, err
}
}
elems = append(elems, val)
i++
}
if len(elems) == 0 {
// Prefer a concrete type over a dynamic type when returning an
// empty set
if ety == cty.DynamicPseudoType {
ety = val.Type().ElementType()
}
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)
elemPath := append(path.Copy(), nil)
it := val.ElementIterator()
for it.Next() {
key, val := it.Element()
var err error
elemPath[len(elemPath)-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, elemPath.NewErrorf("cannot convert key type %s to string for map", key.Type().FriendlyName())
}
if conv != nil {
val, err = conv(val, elemPath)
if err != nil {
return cty.NilVal, err
}
}
elems[keyStr.AsString()] = val
}
if len(elems) == 0 {
// Prefer a concrete type over a dynamic type when returning an
// empty map
if ety == cty.DynamicPseudoType {
ety = val.Type().ElementType()
}
return cty.MapValEmpty(ety), nil
}
if ety.IsCollectionType() || ety.IsObjectType() {
var err error
if elems, err = conversionUnifyCollectionElements(elems, path, false); err != nil {
return cty.NilVal, err
}
}
if err := conversionCheckMapElementTypes(elems, path); err != nil {
return cty.NilVal, err
}
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))
elemPath := append(path.Copy(), nil)
i := int64(0)
it := val.ElementIterator()
for it.Next() {
_, val := it.Element()
var err error
elemPath[len(elemPath)-1] = cty.IndexStep{
Key: cty.NumberIntVal(i),
}
conv := elemConvs[i]
if conv != nil {
val, err = conv(val, elemPath)
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))
elemPath := append(path.Copy(), nil)
i := int64(0)
it := val.ElementIterator()
for it.Next() {
_, val := it.Element()
var err error
elemPath[len(elemPath)-1] = cty.IndexStep{
Key: cty.NumberIntVal(i),
}
conv := elemConvs[i]
if conv != nil {
val, err = conv(val, elemPath)
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))
elemPath := append(path.Copy(), nil)
it := val.ElementIterator()
for it.Next() {
name, val := it.Element()
var err error
elemPath[len(elemPath)-1] = cty.IndexStep{
Key: name,
}
conv := elemConvs[name.AsString()]
if conv != nil {
val, err = conv(val, elemPath)
if err != nil {
return cty.NilVal, err
}
}
elems[name.AsString()] = val
}
if mapEty.IsCollectionType() || mapEty.IsObjectType() {
var err error
if elems, err = conversionUnifyCollectionElements(elems, path, unsafe); err != nil {
return cty.NilVal, err
}
}
if err := conversionCheckMapElementTypes(elems, path); err != nil {
return cty.NilVal, err
}
return cty.MapVal(elems), nil
}
}
// conversionMapToObject returns a conversion that will take a value of the
// given map type and return an object of the given type. The object attribute
// types must all be compatible with the map element type.
//
// Will panic if the given mapType and objType are not maps and objects
// respectively.
func conversionMapToObject(mapType cty.Type, objType cty.Type, unsafe bool) conversion {
objectAtys := objType.AttributeTypes()
mapEty := mapType.ElementType()
elemConvs := make(map[string]conversion, len(objectAtys))
for name, objectAty := range objectAtys {
if objectAty.Equals(mapEty) {
// no conversion required
continue
}
elemConvs[name] = getConversion(mapEty, objectAty, 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))
elemPath := append(path.Copy(), nil)
it := val.ElementIterator()
for it.Next() {
name, val := it.Element()
// if there is no corresponding attribute, we skip this key
if _, ok := objectAtys[name.AsString()]; !ok {
continue
}
var err error
elemPath[len(elemPath)-1] = cty.IndexStep{
Key: name,
}
conv := elemConvs[name.AsString()]
if conv != nil {
val, err = conv(val, elemPath)
if err != nil {
return cty.NilVal, err
}
}
elems[name.AsString()] = val
}
return cty.ObjectVal(elems), nil
}
}
func conversionUnifyCollectionElements(elems map[string]cty.Value, path cty.Path, unsafe bool) (map[string]cty.Value, error) {
elemTypes := make([]cty.Type, 0, len(elems))
for _, elem := range elems {
elemTypes = append(elemTypes, elem.Type())
}
unifiedType, _ := unify(elemTypes, unsafe)
if unifiedType == cty.NilType {
}
unifiedElems := make(map[string]cty.Value)
elemPath := append(path.Copy(), nil)
for name, elem := range elems {
if elem.Type().Equals(unifiedType) {
unifiedElems[name] = elem
continue
}
conv := getConversion(elem.Type(), unifiedType, unsafe)
if conv == nil {
}
elemPath[len(elemPath)-1] = cty.IndexStep{
Key: cty.StringVal(name),
}
val, err := conv(elem, elemPath)
if err != nil {
return nil, err
}
unifiedElems[name] = val
}
return unifiedElems, nil
}
func conversionCheckMapElementTypes(elems map[string]cty.Value, path cty.Path) error {
elementType := cty.NilType
elemPath := append(path.Copy(), nil)
for name, elem := range elems {
if elementType == cty.NilType {
elementType = elem.Type()
continue
}
if !elementType.Equals(elem.Type()) {
elemPath[len(elemPath)-1] = cty.IndexStep{
Key: cty.StringVal(name),
}
return elemPath.NewErrorf("%s is required", elementType.FriendlyName())
}
}
return nil
}

View File

@ -0,0 +1,33 @@
package convert
import (
"github.com/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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))
}

View File

@ -0,0 +1,83 @@
package convert
import (
"errors"
"github.com/hashicorp/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/hashicorp/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
}

357
vendor/github.com/hashicorp/go-cty/cty/convert/unify.go generated vendored Normal file
View File

@ -0,0 +1,357 @@
package convert
import (
"github.com/hashicorp/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.
{
mapCt := 0
objectCt := 0
tupleCt := 0
dynamicCt := 0
for _, ty := range types {
switch {
case ty.IsMapType():
mapCt++
case ty.IsObjectType():
objectCt++
case ty.IsTupleType():
tupleCt++
case ty == cty.DynamicPseudoType:
dynamicCt++
default:
break
}
}
switch {
case mapCt > 0 && (mapCt+dynamicCt) == len(types):
return unifyMapTypes(types, unsafe, dynamicCt > 0)
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 unifyMapTypes(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)
}
elemTypes := make([]cty.Type, 0, len(types))
for _, ty := range types {
elemTypes = append(elemTypes, ty.ElementType())
}
retElemType, _ := unify(elemTypes, unsafe)
if retElemType == cty.NilType {
return cty.NilType, nil
}
retTy := cty.Map(retElemType)
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 cty.NilType, nil
}
}
return retTy, conversions
}
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/hashicorp/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/hashicorp/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/hashicorp/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,
}
}

204
vendor/github.com/hashicorp/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/hashicorp/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,
}
}

7
vendor/github.com/hashicorp/go-cty/cty/gocty/doc.go generated vendored Normal file
View File

@ -0,0 +1,7 @@
// Package gocty deals with converting between cty Values and native go
// values.
//
// It operates under a similar principle to the encoding/json and
// encoding/xml packages in the standard library, using reflection to
// populate native Go data structures from cty values and vice-versa.
package gocty

View File

@ -0,0 +1,43 @@
package gocty
import (
"math/big"
"reflect"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/go-cty/cty/set"
)
var valueType = reflect.TypeOf(cty.Value{})
var typeType = reflect.TypeOf(cty.Type{})
var setType = reflect.TypeOf(set.Set{})
var bigFloatType = reflect.TypeOf(big.Float{})
var bigIntType = reflect.TypeOf(big.Int{})
var emptyInterfaceType = reflect.TypeOf(interface{}(nil))
var stringType = reflect.TypeOf("")
// structTagIndices interrogates the fields of the given type (which must
// be a struct type, or we'll panic) and returns a map from the cty
// attribute names declared via struct tags to the indices of the
// fields holding those tags.
//
// This function will panic if two fields within the struct are tagged with
// the same cty attribute name.
func structTagIndices(st reflect.Type) map[string]int {
ct := st.NumField()
ret := make(map[string]int, ct)
for i := 0; i < ct; i++ {
field := st.Field(i)
attrName := field.Tag.Get("cty")
if attrName != "" {
ret[attrName] = i
}
}
return ret
}

548
vendor/github.com/hashicorp/go-cty/cty/gocty/in.go generated vendored Normal file
View File

@ -0,0 +1,548 @@
package gocty
import (
"math/big"
"reflect"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/go-cty/cty/convert"
"github.com/hashicorp/go-cty/cty/set"
)
// ToCtyValue produces a cty.Value from a Go value. The result will conform
// to the given type, or an error will be returned if this is not possible.
//
// The target type serves as a hint to resolve ambiguities in the mapping.
// For example, the Go type set.Set tells us that the value is a set but
// does not describe the set's element type. This also allows for convenient
// conversions, such as populating a set from a slice rather than having to
// first explicitly instantiate a set.Set.
//
// The audience of this function is assumed to be the developers of Go code
// that is integrating with cty, and thus the error messages it returns are
// presented from Go's perspective. These messages are thus not appropriate
// for display to end-users. An error returned from ToCtyValue represents a
// bug in the calling program, not user error.
func ToCtyValue(val interface{}, ty cty.Type) (cty.Value, error) {
// 'path' starts off as empty but will grow for each level of recursive
// call we make, so by the time toCtyValue returns it is likely to have
// unused capacity on the end of it, depending on how deeply-recursive
// the given Type is.
path := make(cty.Path, 0)
return toCtyValue(reflect.ValueOf(val), ty, path)
}
func toCtyValue(val reflect.Value, ty cty.Type, path cty.Path) (cty.Value, error) {
if val != (reflect.Value{}) && val.Type().AssignableTo(valueType) {
// If the source value is a cty.Value then we'll try to just pass
// through to the target type directly.
return toCtyPassthrough(val, ty, path)
}
switch ty {
case cty.Bool:
return toCtyBool(val, path)
case cty.Number:
return toCtyNumber(val, path)
case cty.String:
return toCtyString(val, path)
case cty.DynamicPseudoType:
return toCtyDynamic(val, path)
}
switch {
case ty.IsListType():
return toCtyList(val, ty.ElementType(), path)
case ty.IsMapType():
return toCtyMap(val, ty.ElementType(), path)
case ty.IsSetType():
return toCtySet(val, ty.ElementType(), path)
case ty.IsObjectType():
return toCtyObject(val, ty.AttributeTypes(), path)
case ty.IsTupleType():
return toCtyTuple(val, ty.TupleElementTypes(), path)
case ty.IsCapsuleType():
return toCtyCapsule(val, ty, path)
}
// We should never fall out here
return cty.NilVal, path.NewErrorf("unsupported target type %#v", ty)
}
func toCtyBool(val reflect.Value, path cty.Path) (cty.Value, error) {
if val = toCtyUnwrapPointer(val); !val.IsValid() {
return cty.NullVal(cty.Bool), nil
}
switch val.Kind() {
case reflect.Bool:
return cty.BoolVal(val.Bool()), nil
default:
return cty.NilVal, path.NewErrorf("can't convert Go %s to bool", val.Kind())
}
}
func toCtyNumber(val reflect.Value, path cty.Path) (cty.Value, error) {
if val = toCtyUnwrapPointer(val); !val.IsValid() {
return cty.NullVal(cty.Number), nil
}
switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return cty.NumberIntVal(val.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return cty.NumberUIntVal(val.Uint()), nil
case reflect.Float32, reflect.Float64:
return cty.NumberFloatVal(val.Float()), nil
case reflect.Struct:
if val.Type().AssignableTo(bigIntType) {
bigInt := val.Interface().(big.Int)
bigFloat := (&big.Float{}).SetInt(&bigInt)
val = reflect.ValueOf(*bigFloat)
}
if val.Type().AssignableTo(bigFloatType) {
bigFloat := val.Interface().(big.Float)
return cty.NumberVal(&bigFloat), nil
}
fallthrough
default:
return cty.NilVal, path.NewErrorf("can't convert Go %s to number", val.Kind())
}
}
func toCtyString(val reflect.Value, path cty.Path) (cty.Value, error) {
if val = toCtyUnwrapPointer(val); !val.IsValid() {
return cty.NullVal(cty.String), nil
}
switch val.Kind() {
case reflect.String:
return cty.StringVal(val.String()), nil
default:
return cty.NilVal, path.NewErrorf("can't convert Go %s to string", val.Kind())
}
}
func toCtyList(val reflect.Value, ety cty.Type, path cty.Path) (cty.Value, error) {
if val = toCtyUnwrapPointer(val); !val.IsValid() {
return cty.NullVal(cty.List(ety)), nil
}
switch val.Kind() {
case reflect.Slice:
if val.IsNil() {
return cty.NullVal(cty.List(ety)), nil
}
fallthrough
case reflect.Array:
if val.Len() == 0 {
return cty.ListValEmpty(ety), nil
}
// While we work on our elements we'll temporarily grow
// path to give us a place to put our index step.
path = append(path, cty.PathStep(nil))
vals := make([]cty.Value, val.Len())
for i := range vals {
var err error
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(int64(i)),
}
vals[i], err = toCtyValue(val.Index(i), ety, path)
if err != nil {
return cty.NilVal, err
}
}
// Discard our extra path segment, retaining it as extra capacity
// for future appending to the path.
path = path[:len(path)-1]
return cty.ListVal(vals), nil
default:
return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Kind(), cty.List(ety))
}
}
func toCtyMap(val reflect.Value, ety cty.Type, path cty.Path) (cty.Value, error) {
if val = toCtyUnwrapPointer(val); !val.IsValid() {
return cty.NullVal(cty.Map(ety)), nil
}
switch val.Kind() {
case reflect.Map:
if val.IsNil() {
return cty.NullVal(cty.Map(ety)), nil
}
if val.Len() == 0 {
return cty.MapValEmpty(ety), nil
}
keyType := val.Type().Key()
if keyType.Kind() != reflect.String {
return cty.NilVal, path.NewErrorf("can't convert Go map with key type %s; key type must be string", keyType)
}
// While we work on our elements we'll temporarily grow
// path to give us a place to put our index step.
path = append(path, cty.PathStep(nil))
vals := make(map[string]cty.Value, val.Len())
for _, kv := range val.MapKeys() {
k := kv.String()
var err error
path[len(path)-1] = cty.IndexStep{
Key: cty.StringVal(k),
}
vals[k], err = toCtyValue(val.MapIndex(reflect.ValueOf(k)), ety, path)
if err != nil {
return cty.NilVal, err
}
}
// Discard our extra path segment, retaining it as extra capacity
// for future appending to the path.
path = path[:len(path)-1]
return cty.MapVal(vals), nil
default:
return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Kind(), cty.Map(ety))
}
}
func toCtySet(val reflect.Value, ety cty.Type, path cty.Path) (cty.Value, error) {
if val = toCtyUnwrapPointer(val); !val.IsValid() {
return cty.NullVal(cty.Set(ety)), nil
}
var vals []cty.Value
switch val.Kind() {
case reflect.Slice:
if val.IsNil() {
return cty.NullVal(cty.Set(ety)), nil
}
fallthrough
case reflect.Array:
if val.Len() == 0 {
return cty.SetValEmpty(ety), nil
}
vals = make([]cty.Value, val.Len())
for i := range vals {
var err error
vals[i], err = toCtyValue(val.Index(i), ety, path)
if err != nil {
return cty.NilVal, err
}
}
case reflect.Struct:
if !val.Type().AssignableTo(setType) {
return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Type(), cty.Set(ety))
}
rawSet := val.Interface().(set.Set)
inVals := rawSet.Values()
if len(inVals) == 0 {
return cty.SetValEmpty(ety), nil
}
vals = make([]cty.Value, len(inVals))
for i := range inVals {
var err error
vals[i], err = toCtyValue(reflect.ValueOf(inVals[i]), ety, path)
if err != nil {
return cty.NilVal, err
}
}
default:
return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Kind(), cty.Set(ety))
}
return cty.SetVal(vals), nil
}
func toCtyObject(val reflect.Value, attrTypes map[string]cty.Type, path cty.Path) (cty.Value, error) {
if val = toCtyUnwrapPointer(val); !val.IsValid() {
return cty.NullVal(cty.Object(attrTypes)), nil
}
switch val.Kind() {
case reflect.Map:
if val.IsNil() {
return cty.NullVal(cty.Object(attrTypes)), nil
}
keyType := val.Type().Key()
if keyType.Kind() != reflect.String {
return cty.NilVal, path.NewErrorf("can't convert Go map with key type %s; key type must be string", keyType)
}
if len(attrTypes) == 0 {
return cty.EmptyObjectVal, nil
}
// While we work on our elements we'll temporarily grow
// path to give us a place to put our GetAttr step.
path = append(path, cty.PathStep(nil))
haveKeys := make(map[string]struct{}, val.Len())
for _, kv := range val.MapKeys() {
haveKeys[kv.String()] = struct{}{}
}
vals := make(map[string]cty.Value, len(attrTypes))
for k, at := range attrTypes {
var err error
path[len(path)-1] = cty.GetAttrStep{
Name: k,
}
if _, have := haveKeys[k]; !have {
vals[k] = cty.NullVal(at)
continue
}
vals[k], err = toCtyValue(val.MapIndex(reflect.ValueOf(k)), at, path)
if err != nil {
return cty.NilVal, err
}
}
// Discard our extra path segment, retaining it as extra capacity
// for future appending to the path.
path = path[:len(path)-1]
return cty.ObjectVal(vals), nil
case reflect.Struct:
if len(attrTypes) == 0 {
return cty.EmptyObjectVal, nil
}
// While we work on our elements we'll temporarily grow
// path to give us a place to put our GetAttr step.
path = append(path, cty.PathStep(nil))
attrFields := structTagIndices(val.Type())
vals := make(map[string]cty.Value, len(attrTypes))
for k, at := range attrTypes {
path[len(path)-1] = cty.GetAttrStep{
Name: k,
}
if fieldIdx, have := attrFields[k]; have {
var err error
vals[k], err = toCtyValue(val.Field(fieldIdx), at, path)
if err != nil {
return cty.NilVal, err
}
} else {
vals[k] = cty.NullVal(at)
}
}
// Discard our extra path segment, retaining it as extra capacity
// for future appending to the path.
path = path[:len(path)-1]
return cty.ObjectVal(vals), nil
default:
return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Kind(), cty.Object(attrTypes))
}
}
func toCtyTuple(val reflect.Value, elemTypes []cty.Type, path cty.Path) (cty.Value, error) {
if val = toCtyUnwrapPointer(val); !val.IsValid() {
return cty.NullVal(cty.Tuple(elemTypes)), nil
}
switch val.Kind() {
case reflect.Slice:
if val.IsNil() {
return cty.NullVal(cty.Tuple(elemTypes)), nil
}
if val.Len() != len(elemTypes) {
return cty.NilVal, path.NewErrorf("wrong number of elements %d; need %d", val.Len(), len(elemTypes))
}
if len(elemTypes) == 0 {
return cty.EmptyTupleVal, nil
}
// While we work on our elements we'll temporarily grow
// path to give us a place to put our Index step.
path = append(path, cty.PathStep(nil))
vals := make([]cty.Value, len(elemTypes))
for i, ety := range elemTypes {
var err error
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(int64(i)),
}
vals[i], err = toCtyValue(val.Index(i), ety, path)
if err != nil {
return cty.NilVal, err
}
}
// Discard our extra path segment, retaining it as extra capacity
// for future appending to the path.
path = path[:len(path)-1]
return cty.TupleVal(vals), nil
case reflect.Struct:
fieldCount := val.Type().NumField()
if fieldCount != len(elemTypes) {
return cty.NilVal, path.NewErrorf("wrong number of struct fields %d; need %d", fieldCount, len(elemTypes))
}
if len(elemTypes) == 0 {
return cty.EmptyTupleVal, nil
}
// While we work on our elements we'll temporarily grow
// path to give us a place to put our Index step.
path = append(path, cty.PathStep(nil))
vals := make([]cty.Value, len(elemTypes))
for i, ety := range elemTypes {
var err error
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(int64(i)),
}
vals[i], err = toCtyValue(val.Field(i), ety, path)
if err != nil {
return cty.NilVal, err
}
}
// Discard our extra path segment, retaining it as extra capacity
// for future appending to the path.
path = path[:len(path)-1]
return cty.TupleVal(vals), nil
default:
return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Kind(), cty.Tuple(elemTypes))
}
}
func toCtyCapsule(val reflect.Value, capsuleType cty.Type, path cty.Path) (cty.Value, error) {
if val = toCtyUnwrapPointer(val); !val.IsValid() {
return cty.NullVal(capsuleType), nil
}
if val.Kind() != reflect.Ptr {
if !val.CanAddr() {
return cty.NilVal, path.NewErrorf("source value for capsule %#v must be addressable", capsuleType)
}
val = val.Addr()
}
if !val.Type().Elem().AssignableTo(capsuleType.EncapsulatedType()) {
return cty.NilVal, path.NewErrorf("value of type %T not compatible with capsule %#v", val.Interface(), capsuleType)
}
return cty.CapsuleVal(capsuleType, val.Interface()), nil
}
func toCtyDynamic(val reflect.Value, path cty.Path) (cty.Value, error) {
if val = toCtyUnwrapPointer(val); !val.IsValid() {
return cty.NullVal(cty.DynamicPseudoType), nil
}
switch val.Kind() {
case reflect.Struct:
if !val.Type().AssignableTo(valueType) {
return cty.NilVal, path.NewErrorf("can't convert Go %s dynamically; only cty.Value allowed", val.Type())
}
return val.Interface().(cty.Value), nil
default:
return cty.NilVal, path.NewErrorf("can't convert Go %s dynamically; only cty.Value allowed", val.Kind())
}
}
func toCtyPassthrough(wrappedVal reflect.Value, wantTy cty.Type, path cty.Path) (cty.Value, error) {
if wrappedVal = toCtyUnwrapPointer(wrappedVal); !wrappedVal.IsValid() {
return cty.NullVal(wantTy), nil
}
givenVal := wrappedVal.Interface().(cty.Value)
val, err := convert.Convert(givenVal, wantTy)
if err != nil {
return cty.NilVal, path.NewErrorf("unsuitable value: %s", err)
}
return val, nil
}
// toCtyUnwrapPointer is a helper for dealing with Go pointers. It has three
// possible outcomes:
//
// - Given value isn't a pointer, so it's just returned as-is.
// - Given value is a non-nil pointer, in which case it is dereferenced
// and the result returned.
// - Given value is a nil pointer, in which case an invalid value is returned.
//
// For nested pointer types, like **int, they are all dereferenced in turn
// until a non-pointer value is found, or until a nil pointer is encountered.
func toCtyUnwrapPointer(val reflect.Value) reflect.Value {
for val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
if val.IsNil() {
return reflect.Value{}
}
val = val.Elem()
}
return val
}

686
vendor/github.com/hashicorp/go-cty/cty/gocty/out.go generated vendored Normal file
View File

@ -0,0 +1,686 @@
package gocty
import (
"math"
"math/big"
"reflect"
"github.com/hashicorp/go-cty/cty"
)
// FromCtyValue assigns a cty.Value to a reflect.Value, which must be a pointer,
// using a fixed set of conversion rules.
//
// This function considers its audience to be the creator of the cty Value
// given, and thus the error messages it generates are (unlike with ToCtyValue)
// presented in cty terminology that is generally appropriate to return to
// end-users in applications where cty data structures are built from
// user-provided configuration. In particular this means that if incorrect
// target types are provided by the calling application the resulting error
// messages are likely to be confusing, since we assume that the given target
// type is correct and the cty.Value is where the error lies.
//
// If an error is returned, the target data structure may have been partially
// populated, but the degree to which this is true is an implementation
// detail that the calling application should not rely on.
//
// The function will panic if given a non-pointer as the Go value target,
// since that is considered to be a bug in the calling program.
func FromCtyValue(val cty.Value, target interface{}) error {
tVal := reflect.ValueOf(target)
if tVal.Kind() != reflect.Ptr {
panic("target value is not a pointer")
}
if tVal.IsNil() {
panic("target value is nil pointer")
}
// 'path' starts off as empty but will grow for each level of recursive
// call we make, so by the time fromCtyValue returns it is likely to have
// unused capacity on the end of it, depending on how deeply-recursive
// the given cty.Value is.
path := make(cty.Path, 0)
return fromCtyValue(val, tVal, path)
}
func fromCtyValue(val cty.Value, target reflect.Value, path cty.Path) error {
ty := val.Type()
deepTarget := fromCtyPopulatePtr(target, false)
// If we're decoding into a cty.Value then we just pass through the
// value as-is, to enable partial decoding. This is the only situation
// where unknown values are permitted.
if deepTarget.Kind() == reflect.Struct && deepTarget.Type().AssignableTo(valueType) {
deepTarget.Set(reflect.ValueOf(val))
return nil
}
// Lists and maps can be nil without indirection, but everything else
// requires a pointer and we set it immediately to nil.
// We also make an exception for capsule types because we want to handle
// pointers specially for these.
// (fromCtyList and fromCtyMap must therefore deal with val.IsNull, while
// other types can assume no nulls after this point.)
if val.IsNull() && !val.Type().IsListType() && !val.Type().IsMapType() && !val.Type().IsCapsuleType() {
target = fromCtyPopulatePtr(target, true)
if target.Kind() != reflect.Ptr {
return path.NewErrorf("null value is not allowed")
}
target.Set(reflect.Zero(target.Type()))
return nil
}
target = deepTarget
if !val.IsKnown() {
return path.NewErrorf("value must be known")
}
switch ty {
case cty.Bool:
return fromCtyBool(val, target, path)
case cty.Number:
return fromCtyNumber(val, target, path)
case cty.String:
return fromCtyString(val, target, path)
}
switch {
case ty.IsListType():
return fromCtyList(val, target, path)
case ty.IsMapType():
return fromCtyMap(val, target, path)
case ty.IsSetType():
return fromCtySet(val, target, path)
case ty.IsObjectType():
return fromCtyObject(val, target, path)
case ty.IsTupleType():
return fromCtyTuple(val, target, path)
case ty.IsCapsuleType():
return fromCtyCapsule(val, target, path)
}
// We should never fall out here; reaching here indicates a bug in this
// function.
return path.NewErrorf("unsupported source type %#v", ty)
}
func fromCtyBool(val cty.Value, target reflect.Value, path cty.Path) error {
switch target.Kind() {
case reflect.Bool:
target.SetBool(val.True())
return nil
default:
return likelyRequiredTypesError(path, target)
}
}
func fromCtyNumber(val cty.Value, target reflect.Value, path cty.Path) error {
bf := val.AsBigFloat()
switch target.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return fromCtyNumberInt(bf, target, path)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return fromCtyNumberUInt(bf, target, path)
case reflect.Float32, reflect.Float64:
return fromCtyNumberFloat(bf, target, path)
case reflect.Struct:
return fromCtyNumberBig(bf, target, path)
default:
return likelyRequiredTypesError(path, target)
}
}
func fromCtyNumberInt(bf *big.Float, target reflect.Value, path cty.Path) error {
// Doing this with switch rather than << arithmetic because << with
// result >32-bits is not portable to 32-bit systems.
var min int64
var max int64
switch target.Type().Bits() {
case 8:
min = math.MinInt8
max = math.MaxInt8
case 16:
min = math.MinInt16
max = math.MaxInt16
case 32:
min = math.MinInt32
max = math.MaxInt32
case 64:
min = math.MinInt64
max = math.MaxInt64
default:
panic("weird number of bits in target int")
}
iv, accuracy := bf.Int64()
if accuracy != big.Exact || iv < min || iv > max {
return path.NewErrorf("value must be a whole number, between %d and %d", min, max)
}
target.SetInt(iv)
return nil
}
func fromCtyNumberUInt(bf *big.Float, target reflect.Value, path cty.Path) error {
// Doing this with switch rather than << arithmetic because << with
// result >32-bits is not portable to 32-bit systems.
var max uint64
switch target.Type().Bits() {
case 8:
max = math.MaxUint8
case 16:
max = math.MaxUint16
case 32:
max = math.MaxUint32
case 64:
max = math.MaxUint64
default:
panic("weird number of bits in target uint")
}
iv, accuracy := bf.Uint64()
if accuracy != big.Exact || iv > max {
return path.NewErrorf("value must be a whole number, between 0 and %d inclusive", max)
}
target.SetUint(iv)
return nil
}
func fromCtyNumberFloat(bf *big.Float, target reflect.Value, path cty.Path) error {
switch target.Kind() {
case reflect.Float32, reflect.Float64:
fv, accuracy := bf.Float64()
if accuracy != big.Exact {
// We allow the precision to be truncated as part of our conversion,
// but we don't want to silently introduce infinities.
if math.IsInf(fv, 0) {
return path.NewErrorf("value must be between %f and %f inclusive", -math.MaxFloat64, math.MaxFloat64)
}
}
target.SetFloat(fv)
return nil
default:
panic("unsupported kind of float")
}
}
func fromCtyNumberBig(bf *big.Float, target reflect.Value, path cty.Path) error {
switch {
case bigFloatType.ConvertibleTo(target.Type()):
// Easy!
target.Set(reflect.ValueOf(bf).Elem().Convert(target.Type()))
return nil
case bigIntType.ConvertibleTo(target.Type()):
bi, accuracy := bf.Int(nil)
if accuracy != big.Exact {
return path.NewErrorf("value must be a whole number")
}
target.Set(reflect.ValueOf(bi).Elem().Convert(target.Type()))
return nil
default:
return likelyRequiredTypesError(path, target)
}
}
func fromCtyString(val cty.Value, target reflect.Value, path cty.Path) error {
switch target.Kind() {
case reflect.String:
target.SetString(val.AsString())
return nil
default:
return likelyRequiredTypesError(path, target)
}
}
func fromCtyList(val cty.Value, target reflect.Value, path cty.Path) error {
switch target.Kind() {
case reflect.Slice:
if val.IsNull() {
target.Set(reflect.Zero(target.Type()))
return nil
}
length := val.LengthInt()
tv := reflect.MakeSlice(target.Type(), length, length)
path = append(path, nil)
i := 0
var err error
val.ForEachElement(func(key cty.Value, val cty.Value) bool {
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(int64(i)),
}
targetElem := tv.Index(i)
err = fromCtyValue(val, targetElem, path)
if err != nil {
return true
}
i++
return false
})
if err != nil {
return err
}
path = path[:len(path)-1]
target.Set(tv)
return nil
case reflect.Array:
if val.IsNull() {
return path.NewErrorf("null value is not allowed")
}
length := val.LengthInt()
if length != target.Len() {
return path.NewErrorf("must be a list of length %d", target.Len())
}
path = append(path, nil)
i := 0
var err error
val.ForEachElement(func(key cty.Value, val cty.Value) bool {
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(int64(i)),
}
targetElem := target.Index(i)
err = fromCtyValue(val, targetElem, path)
if err != nil {
return true
}
i++
return false
})
if err != nil {
return err
}
path = path[:len(path)-1]
return nil
default:
return likelyRequiredTypesError(path, target)
}
}
func fromCtyMap(val cty.Value, target reflect.Value, path cty.Path) error {
switch target.Kind() {
case reflect.Map:
if val.IsNull() {
target.Set(reflect.Zero(target.Type()))
return nil
}
tv := reflect.MakeMap(target.Type())
et := target.Type().Elem()
path = append(path, nil)
var err error
val.ForEachElement(func(key cty.Value, val cty.Value) bool {
path[len(path)-1] = cty.IndexStep{
Key: key,
}
ks := key.AsString()
targetElem := reflect.New(et)
err = fromCtyValue(val, targetElem, path)
tv.SetMapIndex(reflect.ValueOf(ks), targetElem.Elem())
return err != nil
})
if err != nil {
return err
}
path = path[:len(path)-1]
target.Set(tv)
return nil
default:
return likelyRequiredTypesError(path, target)
}
}
func fromCtySet(val cty.Value, target reflect.Value, path cty.Path) error {
switch target.Kind() {
case reflect.Slice:
if val.IsNull() {
target.Set(reflect.Zero(target.Type()))
return nil
}
length := val.LengthInt()
tv := reflect.MakeSlice(target.Type(), length, length)
i := 0
var err error
val.ForEachElement(func(key cty.Value, val cty.Value) bool {
targetElem := tv.Index(i)
err = fromCtyValue(val, targetElem, path)
if err != nil {
return true
}
i++
return false
})
if err != nil {
return err
}
target.Set(tv)
return nil
case reflect.Array:
if val.IsNull() {
return path.NewErrorf("null value is not allowed")
}
length := val.LengthInt()
if length != target.Len() {
return path.NewErrorf("must be a set of length %d", target.Len())
}
i := 0
var err error
val.ForEachElement(func(key cty.Value, val cty.Value) bool {
targetElem := target.Index(i)
err = fromCtyValue(val, targetElem, path)
if err != nil {
return true
}
i++
return false
})
if err != nil {
return err
}
return nil
// TODO: decode into set.Set instance
default:
return likelyRequiredTypesError(path, target)
}
}
func fromCtyObject(val cty.Value, target reflect.Value, path cty.Path) error {
switch target.Kind() {
case reflect.Struct:
attrTypes := val.Type().AttributeTypes()
targetFields := structTagIndices(target.Type())
path = append(path, nil)
for k, i := range targetFields {
if _, exists := attrTypes[k]; !exists {
// If the field in question isn't able to represent nil,
// that's an error.
fk := target.Field(i).Kind()
switch fk {
case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface:
// okay
default:
return path.NewErrorf("missing required attribute %q", k)
}
}
}
for k := range attrTypes {
path[len(path)-1] = cty.GetAttrStep{
Name: k,
}
fieldIdx, exists := targetFields[k]
if !exists {
return path.NewErrorf("unsupported attribute %q", k)
}
ev := val.GetAttr(k)
targetField := target.Field(fieldIdx)
err := fromCtyValue(ev, targetField, path)
if err != nil {
return err
}
}
path = path[:len(path)-1]
return nil
default:
return likelyRequiredTypesError(path, target)
}
}
func fromCtyTuple(val cty.Value, target reflect.Value, path cty.Path) error {
switch target.Kind() {
case reflect.Struct:
elemTypes := val.Type().TupleElementTypes()
fieldCount := target.Type().NumField()
if fieldCount != len(elemTypes) {
return path.NewErrorf("a tuple of %d elements is required", fieldCount)
}
path = append(path, nil)
for i := range elemTypes {
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(int64(i)),
}
ev := val.Index(cty.NumberIntVal(int64(i)))
targetField := target.Field(i)
err := fromCtyValue(ev, targetField, path)
if err != nil {
return err
}
}
path = path[:len(path)-1]
return nil
default:
return likelyRequiredTypesError(path, target)
}
}
func fromCtyCapsule(val cty.Value, target reflect.Value, path cty.Path) error {
if target.Kind() == reflect.Ptr {
// Walk through indirection until we get to the last pointer,
// which we might set to null below.
target = fromCtyPopulatePtr(target, true)
if val.IsNull() {
target.Set(reflect.Zero(target.Type()))
return nil
}
// Since a capsule contains a pointer to an object, we'll preserve
// that pointer on the way out and thus allow the caller to recover
// the original object, rather than a copy of it.
eType := val.Type().EncapsulatedType()
if !eType.AssignableTo(target.Elem().Type()) {
// Our interface contract promises that we won't expose Go
// implementation details in error messages, so we need to keep
// this vague. This can only arise if a calling application has
// more than one capsule type in play and a user mixes them up.
return path.NewErrorf("incorrect type %s", val.Type().FriendlyName())
}
target.Set(reflect.ValueOf(val.EncapsulatedValue()))
return nil
} else {
if val.IsNull() {
return path.NewErrorf("null value is not allowed")
}
// If our target isn't a pointer then we will attempt to copy
// the encapsulated value into it.
eType := val.Type().EncapsulatedType()
if !eType.AssignableTo(target.Type()) {
// Our interface contract promises that we won't expose Go
// implementation details in error messages, so we need to keep
// this vague. This can only arise if a calling application has
// more than one capsule type in play and a user mixes them up.
return path.NewErrorf("incorrect type %s", val.Type().FriendlyName())
}
// We know that EncapsulatedValue is always a pointer, so we
// can safely call .Elem on its reflect.Value.
target.Set(reflect.ValueOf(val.EncapsulatedValue()).Elem())
return nil
}
}
// fromCtyPopulatePtr recognizes when target is a pointer type and allocates
// a value to assign to that pointer, which it returns.
//
// If the given value has multiple levels of indirection, like **int, these
// will be processed in turn so that the return value is guaranteed to be
// a non-pointer.
//
// As an exception, if decodingNull is true then the returned value will be
// the final level of pointer, if any, so that the caller can assign it
// as nil to represent a null value. If the given target value is not a pointer
// at all then the returned value will be just the given target, so the caller
// must test if the returned value is a pointer before trying to assign nil
// to it.
func fromCtyPopulatePtr(target reflect.Value, decodingNull bool) reflect.Value {
for {
if target.Kind() == reflect.Interface && !target.IsNil() {
e := target.Elem()
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
target = e
}
}
if target.Kind() != reflect.Ptr {
break
}
// Stop early if we're decodingNull and we've found our last indirection
if target.Elem().Kind() != reflect.Ptr && decodingNull && target.CanSet() {
break
}
if target.IsNil() {
target.Set(reflect.New(target.Type().Elem()))
}
target = target.Elem()
}
return target
}
// likelyRequiredTypesError returns an error that states which types are
// acceptable by making some assumptions about what types we support for
// each target Go kind. It's not a precise science but it allows us to return
// an error message that is cty-user-oriented rather than Go-oriented.
//
// Generally these error messages should be a matter of last resort, since
// the calling application should be validating user-provided value types
// before decoding anyway.
func likelyRequiredTypesError(path cty.Path, target reflect.Value) error {
switch target.Kind() {
case reflect.Bool:
return path.NewErrorf("bool value is required")
case reflect.String:
return path.NewErrorf("string value is required")
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fallthrough
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
fallthrough
case reflect.Float32, reflect.Float64:
return path.NewErrorf("number value is required")
case reflect.Slice, reflect.Array:
return path.NewErrorf("list or set value is required")
case reflect.Map:
return path.NewErrorf("map or object value is required")
case reflect.Struct:
switch {
case target.Type().AssignableTo(bigFloatType) || target.Type().AssignableTo(bigIntType):
return path.NewErrorf("number value is required")
case target.Type().AssignableTo(setType):
return path.NewErrorf("set or list value is required")
default:
return path.NewErrorf("object or tuple value is required")
}
default:
// We should avoid getting into this path, since this error
// message is rather useless.
return path.NewErrorf("incorrect type")
}
}

View File

@ -0,0 +1,108 @@
package gocty
import (
"reflect"
"github.com/hashicorp/go-cty/cty"
)
// ImpliedType takes an arbitrary Go value (as an interface{}) and attempts
// to find a suitable cty.Type instance that could be used for a conversion
// with ToCtyValue.
//
// This allows -- for simple situations at least -- types to be defined just
// once in Go and the cty types derived from the Go types, but in the process
// it makes some assumptions that may be undesirable so applications are
// encouraged to build their cty types directly if exacting control is
// required.
//
// Not all Go types can be represented as cty types, so an error may be
// returned which is usually considered to be a bug in the calling program.
// In particular, ImpliedType will never use capsule types in its returned
// type, because it cannot know the capsule types supported by the calling
// program.
func ImpliedType(gv interface{}) (cty.Type, error) {
rt := reflect.TypeOf(gv)
var path cty.Path
return impliedType(rt, path)
}
func impliedType(rt reflect.Type, path cty.Path) (cty.Type, error) {
switch rt.Kind() {
case reflect.Ptr:
return impliedType(rt.Elem(), path)
// Primitive types
case reflect.Bool:
return cty.Bool, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return cty.Number, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return cty.Number, nil
case reflect.Float32, reflect.Float64:
return cty.Number, nil
case reflect.String:
return cty.String, nil
// Collection types
case reflect.Slice:
path := append(path, cty.IndexStep{Key: cty.UnknownVal(cty.Number)})
ety, err := impliedType(rt.Elem(), path)
if err != nil {
return cty.NilType, err
}
return cty.List(ety), nil
case reflect.Map:
if !stringType.AssignableTo(rt.Key()) {
return cty.NilType, path.NewErrorf("no cty.Type for %s (must have string keys)", rt)
}
path := append(path, cty.IndexStep{Key: cty.UnknownVal(cty.String)})
ety, err := impliedType(rt.Elem(), path)
if err != nil {
return cty.NilType, err
}
return cty.Map(ety), nil
// Structural types
case reflect.Struct:
return impliedStructType(rt, path)
default:
return cty.NilType, path.NewErrorf("no cty.Type for %s", rt)
}
}
func impliedStructType(rt reflect.Type, path cty.Path) (cty.Type, error) {
if valueType.AssignableTo(rt) {
// Special case: cty.Value represents cty.DynamicPseudoType, for
// type conformance checking.
return cty.DynamicPseudoType, nil
}
fieldIdxs := structTagIndices(rt)
if len(fieldIdxs) == 0 {
return cty.NilType, path.NewErrorf("no cty.Type for %s (no cty field tags)", rt)
}
atys := make(map[string]cty.Type, len(fieldIdxs))
{
// Temporary extension of path for attributes
path := append(path, nil)
for k, fi := range fieldIdxs {
path[len(path)-1] = cty.GetAttrStep{Name: k}
ft := rt.Field(fi).Type
aty, err := impliedType(ft, path)
if err != nil {
return cty.NilType, err
}
atys[k] = aty
}
}
return cty.Object(atys), nil
}

99
vendor/github.com/hashicorp/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/hashicorp/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")
}
}

11
vendor/github.com/hashicorp/go-cty/cty/json/doc.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
// Package json provides functions for serializing cty types and values in
// JSON format, and for decoding them again.
//
// Since the cty type system is a superset of the JSON type system,
// round-tripping through JSON is lossy unless type information is provided
// both at encoding time and decoding time. Callers of this package are
// therefore suggested to define their expected structure as a cty.Type
// and pass it in consistently both when encoding and when decoding, though
// default (type-lossy) behavior is provided for situations where the precise
// representation of the data is not significant.
package json

193
vendor/github.com/hashicorp/go-cty/cty/json/marshal.go generated vendored Normal file
View File

@ -0,0 +1,193 @@
package json
import (
"bytes"
"encoding/json"
"sort"
"github.com/hashicorp/go-cty/cty"
)
func marshal(val cty.Value, t cty.Type, path cty.Path, b *bytes.Buffer) error {
if val.IsMarked() {
return path.NewErrorf("value has marks, so it cannot be seralized")
}
// If we're going to decode as DynamicPseudoType then we need to save
// dynamic type information to recover the real type.
if t == cty.DynamicPseudoType && val.Type() != cty.DynamicPseudoType {
return marshalDynamic(val, path, b)
}
if val.IsNull() {
b.WriteString("null")
return nil
}
if !val.IsKnown() {
return path.NewErrorf("value is not known")
}
// The caller should've guaranteed that the given val is conformant with
// the given type t, so we'll proceed under that assumption here.
switch {
case t.IsPrimitiveType():
switch t {
case cty.String:
json, err := json.Marshal(val.AsString())
if err != nil {
return path.NewErrorf("failed to serialize value: %s", err)
}
b.Write(json)
return nil
case cty.Number:
if val.RawEquals(cty.PositiveInfinity) || val.RawEquals(cty.NegativeInfinity) {
return path.NewErrorf("cannot serialize infinity as JSON")
}
b.WriteString(val.AsBigFloat().Text('f', -1))
return nil
case cty.Bool:
if val.True() {
b.WriteString("true")
} else {
b.WriteString("false")
}
return nil
default:
panic("unsupported primitive type")
}
case t.IsListType(), t.IsSetType():
b.WriteRune('[')
first := true
ety := t.ElementType()
it := val.ElementIterator()
path := append(path, nil) // local override of 'path' with extra element
for it.Next() {
if !first {
b.WriteRune(',')
}
ek, ev := it.Element()
path[len(path)-1] = cty.IndexStep{
Key: ek,
}
err := marshal(ev, ety, path, b)
if err != nil {
return err
}
first = false
}
b.WriteRune(']')
return nil
case t.IsMapType():
b.WriteRune('{')
first := true
ety := t.ElementType()
it := val.ElementIterator()
path := append(path, nil) // local override of 'path' with extra element
for it.Next() {
if !first {
b.WriteRune(',')
}
ek, ev := it.Element()
path[len(path)-1] = cty.IndexStep{
Key: ek,
}
var err error
err = marshal(ek, ek.Type(), path, b)
if err != nil {
return err
}
b.WriteRune(':')
err = marshal(ev, ety, path, b)
if err != nil {
return err
}
first = false
}
b.WriteRune('}')
return nil
case t.IsTupleType():
b.WriteRune('[')
etys := t.TupleElementTypes()
it := val.ElementIterator()
path := append(path, nil) // local override of 'path' with extra element
i := 0
for it.Next() {
if i > 0 {
b.WriteRune(',')
}
ety := etys[i]
ek, ev := it.Element()
path[len(path)-1] = cty.IndexStep{
Key: ek,
}
err := marshal(ev, ety, path, b)
if err != nil {
return err
}
i++
}
b.WriteRune(']')
return nil
case t.IsObjectType():
b.WriteRune('{')
atys := t.AttributeTypes()
path := append(path, nil) // local override of 'path' with extra element
names := make([]string, 0, len(atys))
for k := range atys {
names = append(names, k)
}
sort.Strings(names)
for i, k := range names {
aty := atys[k]
if i > 0 {
b.WriteRune(',')
}
av := val.GetAttr(k)
path[len(path)-1] = cty.GetAttrStep{
Name: k,
}
var err error
err = marshal(cty.StringVal(k), cty.String, path, b)
if err != nil {
return err
}
b.WriteRune(':')
err = marshal(av, aty, path, b)
if err != nil {
return err
}
}
b.WriteRune('}')
return nil
case t.IsCapsuleType():
rawVal := val.EncapsulatedValue()
jsonVal, err := json.Marshal(rawVal)
if err != nil {
return path.NewError(err)
}
b.Write(jsonVal)
return nil
default:
// should never happen
return path.NewErrorf("cannot JSON-serialize %s", t.FriendlyName())
}
}
// marshalDynamic adds an extra wrapping object containing dynamic type
// information for the given value.
func marshalDynamic(val cty.Value, path cty.Path, b *bytes.Buffer) error {
typeJSON, err := MarshalType(val.Type())
if err != nil {
return path.NewErrorf("failed to serialize type: %s", err)
}
b.WriteString(`{"value":`)
marshal(val, val.Type(), path, b)
b.WriteString(`,"type":`)
b.Write(typeJSON)
b.WriteRune('}')
return nil
}

41
vendor/github.com/hashicorp/go-cty/cty/json/simple.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
package json
import (
"github.com/hashicorp/go-cty/cty"
)
// SimpleJSONValue is a wrapper around cty.Value that adds implementations of
// json.Marshaler and json.Unmarshaler for simple-but-type-lossy automatic
// encoding and decoding of values.
//
// The couplet Marshal and Unmarshal both take extra type information to
// inform the encoding and decoding process so that all of the cty types
// can be represented even though JSON's type system is a subset.
//
// SimpleJSONValue instead takes the approach of discarding the value's type
// information and then deriving a new type from the stored structure when
// decoding. This results in the same data being returned but not necessarily
// with exactly the same type.
//
// For information on how types are inferred when decoding, see the
// documentation of the function ImpliedType.
type SimpleJSONValue struct {
cty.Value
}
// MarshalJSON is an implementation of json.Marshaler. See the documentation
// of SimpleJSONValue for more information.
func (v SimpleJSONValue) MarshalJSON() ([]byte, error) {
return Marshal(v.Value, v.Type())
}
// UnmarshalJSON is an implementation of json.Unmarshaler. See the
// documentation of SimpleJSONValue for more information.
func (v *SimpleJSONValue) UnmarshalJSON(buf []byte) error {
t, err := ImpliedType(buf)
if err != nil {
return err
}
v.Value, err = Unmarshal(buf, t)
return err
}

23
vendor/github.com/hashicorp/go-cty/cty/json/type.go generated vendored Normal file
View File

@ -0,0 +1,23 @@
package json
import (
"github.com/hashicorp/go-cty/cty"
)
// MarshalType returns a JSON serialization of the given type.
//
// This is just a thin wrapper around t.MarshalJSON, for symmetry with
// UnmarshalType.
func MarshalType(t cty.Type) ([]byte, error) {
return t.MarshalJSON()
}
// UnmarshalType decodes a JSON serialization of the given type as produced
// by either Type.MarshalJSON or MarshalType.
//
// This is a convenience wrapper around Type.UnmarshalJSON.
func UnmarshalType(buf []byte) (cty.Type, error) {
var t cty.Type
err := t.UnmarshalJSON(buf)
return t, err
}

View File

@ -0,0 +1,170 @@
package json
import (
"bytes"
"encoding/json"
"fmt"
"github.com/hashicorp/go-cty/cty"
)
// ImpliedType returns the cty Type implied by the structure of the given
// JSON-compliant buffer. This function implements the default type mapping
// behavior used when decoding arbitrary JSON without explicit cty Type
// information.
//
// The rules are as follows:
//
// JSON strings, numbers and bools map to their equivalent primitive type in
// cty.
//
// JSON objects map to cty object types, with the attributes defined by the
// object keys and the types of their values.
//
// JSON arrays map to cty tuple types, with the elements defined by the
// types of the array members.
//
// Any nulls are typed as DynamicPseudoType, so callers of this function
// must be prepared to deal with this. Callers that do not wish to deal with
// dynamic typing should not use this function and should instead describe
// their required types explicitly with a cty.Type instance when decoding.
//
// Any JSON syntax errors will be returned as an error, and the type will
// be the invalid value cty.NilType.
func ImpliedType(buf []byte) (cty.Type, error) {
r := bytes.NewReader(buf)
dec := json.NewDecoder(r)
dec.UseNumber()
ty, err := impliedType(dec)
if err != nil {
return cty.NilType, err
}
if dec.More() {
return cty.NilType, fmt.Errorf("extraneous data after JSON object")
}
return ty, nil
}
func impliedType(dec *json.Decoder) (cty.Type, error) {
tok, err := dec.Token()
if err != nil {
return cty.NilType, err
}
return impliedTypeForTok(tok, dec)
}
func impliedTypeForTok(tok json.Token, dec *json.Decoder) (cty.Type, error) {
if tok == nil {
return cty.DynamicPseudoType, nil
}
switch ttok := tok.(type) {
case bool:
return cty.Bool, nil
case json.Number:
return cty.Number, nil
case string:
return cty.String, nil
case json.Delim:
switch rune(ttok) {
case '{':
return impliedObjectType(dec)
case '[':
return impliedTupleType(dec)
default:
return cty.NilType, fmt.Errorf("unexpected token %q", ttok)
}
default:
return cty.NilType, fmt.Errorf("unsupported JSON token %#v", tok)
}
}
func impliedObjectType(dec *json.Decoder) (cty.Type, error) {
// By the time we get in here, we've already consumed the { delimiter
// and so our next token should be the first object key.
var atys map[string]cty.Type
for {
// Read the object key first
tok, err := dec.Token()
if err != nil {
return cty.NilType, err
}
if ttok, ok := tok.(json.Delim); ok {
if rune(ttok) != '}' {
return cty.NilType, fmt.Errorf("unexpected delimiter %q", ttok)
}
break
}
key, ok := tok.(string)
if !ok {
return cty.NilType, fmt.Errorf("expected string but found %T", tok)
}
// Now read the value
tok, err = dec.Token()
if err != nil {
return cty.NilType, err
}
aty, err := impliedTypeForTok(tok, dec)
if err != nil {
return cty.NilType, err
}
if atys == nil {
atys = make(map[string]cty.Type)
}
atys[key] = aty
}
if len(atys) == 0 {
return cty.EmptyObject, nil
}
return cty.Object(atys), nil
}
func impliedTupleType(dec *json.Decoder) (cty.Type, error) {
// By the time we get in here, we've already consumed the [ delimiter
// and so our next token should be the first value.
var etys []cty.Type
for {
tok, err := dec.Token()
if err != nil {
return cty.NilType, err
}
if ttok, ok := tok.(json.Delim); ok {
if rune(ttok) == ']' {
break
}
}
ety, err := impliedTypeForTok(tok, dec)
if err != nil {
return cty.NilType, err
}
etys = append(etys, ety)
}
if len(etys) == 0 {
return cty.EmptyTuple, nil
}
return cty.Tuple(etys), nil
}

View File

@ -0,0 +1,459 @@
package json
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/go-cty/cty/convert"
)
func unmarshal(buf []byte, t cty.Type, path cty.Path) (cty.Value, error) {
dec := bufDecoder(buf)
tok, err := dec.Token()
if err != nil {
return cty.NilVal, path.NewError(err)
}
if tok == nil {
return cty.NullVal(t), nil
}
if t == cty.DynamicPseudoType {
return unmarshalDynamic(buf, path)
}
switch {
case t.IsPrimitiveType():
val, err := unmarshalPrimitive(tok, t, path)
if err != nil {
return cty.NilVal, err
}
return val, nil
case t.IsListType():
return unmarshalList(buf, t.ElementType(), path)
case t.IsSetType():
return unmarshalSet(buf, t.ElementType(), path)
case t.IsMapType():
return unmarshalMap(buf, t.ElementType(), path)
case t.IsTupleType():
return unmarshalTuple(buf, t.TupleElementTypes(), path)
case t.IsObjectType():
return unmarshalObject(buf, t.AttributeTypes(), path)
case t.IsCapsuleType():
return unmarshalCapsule(buf, t, path)
default:
return cty.NilVal, path.NewErrorf("unsupported type %s", t.FriendlyName())
}
}
func unmarshalPrimitive(tok json.Token, t cty.Type, path cty.Path) (cty.Value, error) {
switch t {
case cty.Bool:
switch v := tok.(type) {
case bool:
return cty.BoolVal(v), nil
case string:
val, err := convert.Convert(cty.StringVal(v), t)
if err != nil {
return cty.NilVal, path.NewError(err)
}
return val, nil
default:
return cty.NilVal, path.NewErrorf("bool is required")
}
case cty.Number:
if v, ok := tok.(json.Number); ok {
tok = string(v)
}
switch v := tok.(type) {
case string:
val, err := cty.ParseNumberVal(v)
if err != nil {
return cty.NilVal, path.NewError(err)
}
return val, nil
default:
return cty.NilVal, path.NewErrorf("number is required")
}
case cty.String:
switch v := tok.(type) {
case string:
return cty.StringVal(v), nil
case json.Number:
return cty.StringVal(string(v)), nil
case bool:
val, err := convert.Convert(cty.BoolVal(v), t)
if err != nil {
return cty.NilVal, path.NewError(err)
}
return val, nil
default:
return cty.NilVal, path.NewErrorf("string is required")
}
default:
// should never happen
panic("unsupported primitive type")
}
}
func unmarshalList(buf []byte, ety cty.Type, path cty.Path) (cty.Value, error) {
dec := bufDecoder(buf)
if err := requireDelim(dec, '['); err != nil {
return cty.NilVal, path.NewError(err)
}
var vals []cty.Value
{
path := append(path, nil)
var idx int64
for dec.More() {
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(idx),
}
idx++
rawVal, err := readRawValue(dec)
if err != nil {
return cty.NilVal, path.NewErrorf("failed to read list value: %s", err)
}
el, err := unmarshal(rawVal, ety, path)
if err != nil {
return cty.NilVal, err
}
vals = append(vals, el)
}
}
if err := requireDelim(dec, ']'); err != nil {
return cty.NilVal, path.NewError(err)
}
if len(vals) == 0 {
return cty.ListValEmpty(ety), nil
}
return cty.ListVal(vals), nil
}
func unmarshalSet(buf []byte, ety cty.Type, path cty.Path) (cty.Value, error) {
dec := bufDecoder(buf)
if err := requireDelim(dec, '['); err != nil {
return cty.NilVal, path.NewError(err)
}
var vals []cty.Value
{
path := append(path, nil)
for dec.More() {
path[len(path)-1] = cty.IndexStep{
Key: cty.UnknownVal(ety),
}
rawVal, err := readRawValue(dec)
if err != nil {
return cty.NilVal, path.NewErrorf("failed to read set value: %s", err)
}
el, err := unmarshal(rawVal, ety, path)
if err != nil {
return cty.NilVal, err
}
vals = append(vals, el)
}
}
if err := requireDelim(dec, ']'); err != nil {
return cty.NilVal, path.NewError(err)
}
if len(vals) == 0 {
return cty.SetValEmpty(ety), nil
}
return cty.SetVal(vals), nil
}
func unmarshalMap(buf []byte, ety cty.Type, path cty.Path) (cty.Value, error) {
dec := bufDecoder(buf)
if err := requireDelim(dec, '{'); err != nil {
return cty.NilVal, path.NewError(err)
}
vals := make(map[string]cty.Value)
{
path := append(path, nil)
for dec.More() {
path[len(path)-1] = cty.IndexStep{
Key: cty.UnknownVal(cty.String),
}
var err error
k, err := requireObjectKey(dec)
if err != nil {
return cty.NilVal, path.NewErrorf("failed to read map key: %s", err)
}
path[len(path)-1] = cty.IndexStep{
Key: cty.StringVal(k),
}
rawVal, err := readRawValue(dec)
if err != nil {
return cty.NilVal, path.NewErrorf("failed to read map value: %s", err)
}
el, err := unmarshal(rawVal, ety, path)
if err != nil {
return cty.NilVal, err
}
vals[k] = el
}
}
if err := requireDelim(dec, '}'); err != nil {
return cty.NilVal, path.NewError(err)
}
if len(vals) == 0 {
return cty.MapValEmpty(ety), nil
}
return cty.MapVal(vals), nil
}
func unmarshalTuple(buf []byte, etys []cty.Type, path cty.Path) (cty.Value, error) {
dec := bufDecoder(buf)
if err := requireDelim(dec, '['); err != nil {
return cty.NilVal, path.NewError(err)
}
var vals []cty.Value
{
path := append(path, nil)
var idx int
for dec.More() {
if idx >= len(etys) {
return cty.NilVal, path[:len(path)-1].NewErrorf("too many tuple elements (need %d)", len(etys))
}
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(int64(idx)),
}
ety := etys[idx]
idx++
rawVal, err := readRawValue(dec)
if err != nil {
return cty.NilVal, path.NewErrorf("failed to read tuple value: %s", err)
}
el, err := unmarshal(rawVal, ety, path)
if err != nil {
return cty.NilVal, err
}
vals = append(vals, el)
}
}
if err := requireDelim(dec, ']'); err != nil {
return cty.NilVal, path.NewError(err)
}
if len(vals) != len(etys) {
return cty.NilVal, path[:len(path)-1].NewErrorf("not enough tuple elements (need %d)", len(etys))
}
if len(vals) == 0 {
return cty.EmptyTupleVal, nil
}
return cty.TupleVal(vals), nil
}
func unmarshalObject(buf []byte, atys map[string]cty.Type, path cty.Path) (cty.Value, error) {
dec := bufDecoder(buf)
if err := requireDelim(dec, '{'); err != nil {
return cty.NilVal, path.NewError(err)
}
vals := make(map[string]cty.Value)
{
objPath := path // some errors report from the object's perspective
path := append(path, nil) // path to a specific attribute
for dec.More() {
var err error
k, err := requireObjectKey(dec)
if err != nil {
return cty.NilVal, path.NewErrorf("failed to read object key: %s", err)
}
aty, ok := atys[k]
if !ok {
return cty.NilVal, objPath.NewErrorf("unsupported attribute %q", k)
}
path[len(path)-1] = cty.GetAttrStep{
Name: k,
}
rawVal, err := readRawValue(dec)
if err != nil {
return cty.NilVal, path.NewErrorf("failed to read object value: %s", err)
}
el, err := unmarshal(rawVal, aty, path)
if err != nil {
return cty.NilVal, err
}
vals[k] = el
}
}
if err := requireDelim(dec, '}'); err != nil {
return cty.NilVal, path.NewError(err)
}
// Make sure we have a value for every attribute
for k, aty := range atys {
if _, exists := vals[k]; !exists {
vals[k] = cty.NullVal(aty)
}
}
if len(vals) == 0 {
return cty.EmptyObjectVal, nil
}
return cty.ObjectVal(vals), nil
}
func unmarshalCapsule(buf []byte, t cty.Type, path cty.Path) (cty.Value, error) {
rawType := t.EncapsulatedType()
ptrPtr := reflect.New(reflect.PtrTo(rawType))
ptrPtr.Elem().Set(reflect.New(rawType))
ptr := ptrPtr.Elem().Interface()
err := json.Unmarshal(buf, ptr)
if err != nil {
return cty.NilVal, path.NewError(err)
}
return cty.CapsuleVal(t, ptr), nil
}
func unmarshalDynamic(buf []byte, path cty.Path) (cty.Value, error) {
dec := bufDecoder(buf)
if err := requireDelim(dec, '{'); err != nil {
return cty.NilVal, path.NewError(err)
}
var t cty.Type
var valBody []byte // defer actual decoding until we know the type
for dec.More() {
var err error
key, err := requireObjectKey(dec)
if err != nil {
return cty.NilVal, path.NewErrorf("failed to read dynamic type descriptor key: %s", err)
}
rawVal, err := readRawValue(dec)
if err != nil {
return cty.NilVal, path.NewErrorf("failed to read dynamic type descriptor value: %s", err)
}
switch key {
case "type":
err := json.Unmarshal(rawVal, &t)
if err != nil {
return cty.NilVal, path.NewErrorf("failed to decode type for dynamic value: %s", err)
}
case "value":
valBody = rawVal
default:
return cty.NilVal, path.NewErrorf("invalid key %q in dynamically-typed value", key)
}
}
if err := requireDelim(dec, '}'); err != nil {
return cty.NilVal, path.NewError(err)
}
if t == cty.NilType {
return cty.NilVal, path.NewErrorf("missing type in dynamically-typed value")
}
if valBody == nil {
return cty.NilVal, path.NewErrorf("missing value in dynamically-typed value")
}
val, err := Unmarshal([]byte(valBody), t)
if err != nil {
return cty.NilVal, path.NewError(err)
}
return val, nil
}
func requireDelim(dec *json.Decoder, d rune) error {
tok, err := dec.Token()
if err != nil {
return err
}
if tok != json.Delim(d) {
return fmt.Errorf("missing expected %c", d)
}
return nil
}
func requireObjectKey(dec *json.Decoder) (string, error) {
tok, err := dec.Token()
if err != nil {
return "", err
}
if s, ok := tok.(string); ok {
return s, nil
}
return "", fmt.Errorf("missing expected object key")
}
func readRawValue(dec *json.Decoder) ([]byte, error) {
var rawVal json.RawMessage
err := dec.Decode(&rawVal)
if err != nil {
return nil, err
}
return []byte(rawVal), nil
}
func bufDecoder(buf []byte) *json.Decoder {
r := bytes.NewReader(buf)
dec := json.NewDecoder(r)
dec.UseNumber()
return dec
}

65
vendor/github.com/hashicorp/go-cty/cty/json/value.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
package json
import (
"bytes"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/go-cty/cty/convert"
)
// Marshal produces a JSON representation of the given value that can later
// be decoded into a value of the given type.
//
// A type is specified separately to allow for the given type to include
// cty.DynamicPseudoType to represent situations where any type is permitted
// and so type information must be included to allow recovery of the stored
// structure when decoding.
//
// The given type will also be used to attempt automatic conversions of any
// non-conformant types in the given value, although this will not always
// be possible. If the value cannot be made to be conformant then an error is
// returned, which may be a cty.PathError.
//
// Capsule-typed values can be marshalled, but with some caveats. Since
// capsule values are compared by pointer equality, it is impossible to recover
// a value that will compare equal to the original value. Additionally,
// it's not possible to JSON-serialize the capsule type itself, so it's not
// valid to use capsule types within parts of the value that are conformed to
// cty.DynamicPseudoType. Otherwise, a capsule value can be used as long as
// the encapsulated type itself is serializable with the Marshal function
// in encoding/json.
func Marshal(val cty.Value, t cty.Type) ([]byte, error) {
errs := val.Type().TestConformance(t)
if errs != nil {
// Attempt a conversion
var err error
val, err = convert.Convert(val, t)
if err != nil {
return nil, err
}
}
// From this point onward, val can be assumed to be conforming to t.
buf := &bytes.Buffer{}
var path cty.Path
err := marshal(val, t, path, buf)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Unmarshal decodes a JSON representation of the given value into a cty Value
// conforming to the given type.
//
// While decoding, type conversions will be done where possible to make
// the result conformant even if the types given in JSON are not exactly
// correct. If conversion isn't possible then an error is returned, which
// may be a cty.PathError.
func Unmarshal(buf []byte, t cty.Type) (cty.Value, error) {
var path cty.Path
return unmarshal(buf, t, path)
}

74
vendor/github.com/hashicorp/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/hashicorp/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/hashicorp/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 := v.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/hashicorp/go-cty/cty/msgpack/doc.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
// Package msgpack provides functions for serializing cty values in the
// msgpack encoding, and decoding them again.
//
// If the same type information is provided both at encoding and decoding time
// then values can be round-tripped without loss, except for capsule types
// which are not currently supported.
//
// If any unknown values are passed to Marshal then they will be represented
// using a msgpack extension with type code zero, which is understood by
// the Unmarshal function within this package but will not be understood by
// a generic (non-cty-aware) msgpack decoder. Ensure that no unknown values
// are used if interoperability with other msgpack implementations is
// required.
package msgpack

View File

@ -0,0 +1,31 @@
package msgpack
import (
"bytes"
"github.com/hashicorp/go-cty/cty"
"github.com/vmihailenco/msgpack"
)
type dynamicVal struct {
Value cty.Value
Path cty.Path
}
func (dv *dynamicVal) MarshalMsgpack() ([]byte, error) {
// Rather than defining a msgpack-specific serialization of types,
// instead we use the existing JSON serialization.
typeJSON, err := dv.Value.Type().MarshalJSON()
if err != nil {
return nil, dv.Path.NewErrorf("failed to serialize type: %s", err)
}
var buf bytes.Buffer
enc := msgpack.NewEncoder(&buf)
enc.EncodeArrayLen(2)
enc.EncodeBytes(typeJSON)
err = marshal(dv.Value, dv.Value.Type(), dv.Path, enc)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}

View File

@ -0,0 +1,8 @@
package msgpack
import (
"math"
)
var negativeInfinity = math.Inf(-1)
var positiveInfinity = math.Inf(1)

View File

@ -0,0 +1,211 @@
package msgpack
import (
"bytes"
"math/big"
"sort"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/go-cty/cty/convert"
"github.com/vmihailenco/msgpack"
)
// Marshal produces a msgpack serialization of the given value that
// can be decoded into the given type later using Unmarshal.
//
// The given value must conform to the given type, or an error will
// be returned.
func Marshal(val cty.Value, ty cty.Type) ([]byte, error) {
errs := val.Type().TestConformance(ty)
if errs != nil {
// Attempt a conversion
var err error
val, err = convert.Convert(val, ty)
if err != nil {
return nil, err
}
}
// From this point onward, val can be assumed to be conforming to t.
var path cty.Path
var buf bytes.Buffer
enc := msgpack.NewEncoder(&buf)
err := marshal(val, ty, path, enc)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func marshal(val cty.Value, ty cty.Type, path cty.Path, enc *msgpack.Encoder) error {
if val.IsMarked() {
return path.NewErrorf("value has marks, so it cannot be seralized")
}
// If we're going to decode as DynamicPseudoType then we need to save
// dynamic type information to recover the real type.
if ty == cty.DynamicPseudoType && val.Type() != cty.DynamicPseudoType {
return marshalDynamic(val, path, enc)
}
if !val.IsKnown() {
err := enc.Encode(unknownVal)
if err != nil {
return path.NewError(err)
}
return nil
}
if val.IsNull() {
err := enc.EncodeNil()
if err != nil {
return path.NewError(err)
}
return nil
}
// The caller should've guaranteed that the given val is conformant with
// the given type ty, so we'll proceed under that assumption here.
switch {
case ty.IsPrimitiveType():
switch ty {
case cty.String:
err := enc.EncodeString(val.AsString())
if err != nil {
return path.NewError(err)
}
return nil
case cty.Number:
var err error
switch {
case val.RawEquals(cty.PositiveInfinity):
err = enc.EncodeFloat64(positiveInfinity)
case val.RawEquals(cty.NegativeInfinity):
err = enc.EncodeFloat64(negativeInfinity)
default:
bf := val.AsBigFloat()
if iv, acc := bf.Int64(); acc == big.Exact {
err = enc.EncodeInt(iv)
} else if fv, acc := bf.Float64(); acc == big.Exact {
err = enc.EncodeFloat64(fv)
} else {
err = enc.EncodeString(bf.Text('f', -1))
}
}
if err != nil {
return path.NewError(err)
}
return nil
case cty.Bool:
err := enc.EncodeBool(val.True())
if err != nil {
return path.NewError(err)
}
return nil
default:
panic("unsupported primitive type")
}
case ty.IsListType(), ty.IsSetType():
enc.EncodeArrayLen(val.LengthInt())
ety := ty.ElementType()
it := val.ElementIterator()
path := append(path, nil) // local override of 'path' with extra element
for it.Next() {
ek, ev := it.Element()
path[len(path)-1] = cty.IndexStep{
Key: ek,
}
err := marshal(ev, ety, path, enc)
if err != nil {
return err
}
}
return nil
case ty.IsMapType():
enc.EncodeMapLen(val.LengthInt())
ety := ty.ElementType()
it := val.ElementIterator()
path := append(path, nil) // local override of 'path' with extra element
for it.Next() {
ek, ev := it.Element()
path[len(path)-1] = cty.IndexStep{
Key: ek,
}
var err error
err = marshal(ek, ek.Type(), path, enc)
if err != nil {
return err
}
err = marshal(ev, ety, path, enc)
if err != nil {
return err
}
}
return nil
case ty.IsTupleType():
etys := ty.TupleElementTypes()
it := val.ElementIterator()
path := append(path, nil) // local override of 'path' with extra element
i := 0
enc.EncodeArrayLen(len(etys))
for it.Next() {
ety := etys[i]
ek, ev := it.Element()
path[len(path)-1] = cty.IndexStep{
Key: ek,
}
err := marshal(ev, ety, path, enc)
if err != nil {
return err
}
i++
}
return nil
case ty.IsObjectType():
atys := ty.AttributeTypes()
path := append(path, nil) // local override of 'path' with extra element
names := make([]string, 0, len(atys))
for k := range atys {
names = append(names, k)
}
sort.Strings(names)
enc.EncodeMapLen(len(names))
for _, k := range names {
aty := atys[k]
av := val.GetAttr(k)
path[len(path)-1] = cty.GetAttrStep{
Name: k,
}
var err error
err = marshal(cty.StringVal(k), cty.String, path, enc)
if err != nil {
return err
}
err = marshal(av, aty, path, enc)
if err != nil {
return err
}
}
return nil
case ty.IsCapsuleType():
return path.NewErrorf("capsule types not supported for msgpack encoding")
default:
// should never happen
return path.NewErrorf("cannot msgpack-serialize %s", ty.FriendlyName())
}
}
// marshalDynamic adds an extra wrapping object containing dynamic type
// information for the given value.
func marshalDynamic(val cty.Value, path cty.Path, enc *msgpack.Encoder) error {
dv := dynamicVal{
Value: val,
Path: path,
}
return enc.Encode(&dv)
}

View File

@ -0,0 +1,167 @@
package msgpack
import (
"bytes"
"fmt"
"io"
"github.com/hashicorp/go-cty/cty"
"github.com/vmihailenco/msgpack"
msgpackcodes "github.com/vmihailenco/msgpack/codes"
)
// ImpliedType returns the cty Type implied by the structure of the given
// msgpack-compliant buffer. This function implements the default type mapping
// behavior used when decoding arbitrary msgpack without explicit cty Type
// information.
//
// The rules are as follows:
//
// msgpack strings, numbers and bools map to their equivalent primitive type in
// cty.
//
// msgpack maps become cty object types, with the attributes defined by the
// map keys and the types of their values.
//
// msgpack arrays become cty tuple types, with the elements defined by the
// types of the array members.
//
// Any nulls are typed as DynamicPseudoType, so callers of this function
// must be prepared to deal with this. Callers that do not wish to deal with
// dynamic typing should not use this function and should instead describe
// their required types explicitly with a cty.Type instance when decoding.
//
// Any unknown values are similarly typed as DynamicPseudoType, because these
// do not carry type information on the wire.
//
// Any parse errors will be returned as an error, and the type will be the
// invalid value cty.NilType.
func ImpliedType(buf []byte) (cty.Type, error) {
r := bytes.NewReader(buf)
dec := msgpack.NewDecoder(r)
ty, err := impliedType(dec)
if err != nil {
return cty.NilType, err
}
// We must now be at the end of the buffer
err = dec.Skip()
if err != io.EOF {
return ty, fmt.Errorf("extra bytes after msgpack value")
}
return ty, nil
}
func impliedType(dec *msgpack.Decoder) (cty.Type, error) {
// If this function returns with a nil error then it must have already
// consumed the next value from the decoder, since when called recursively
// the caller will be expecting to find a following value here.
code, err := dec.PeekCode()
if err != nil {
return cty.NilType, err
}
switch {
case code == msgpackcodes.Nil || msgpackcodes.IsExt(code):
err := dec.Skip()
return cty.DynamicPseudoType, err
case code == msgpackcodes.True || code == msgpackcodes.False:
_, err := dec.DecodeBool()
return cty.Bool, err
case msgpackcodes.IsFixedNum(code):
_, err := dec.DecodeInt64()
return cty.Number, err
case code == msgpackcodes.Int8 || code == msgpackcodes.Int16 || code == msgpackcodes.Int32 || code == msgpackcodes.Int64:
_, err := dec.DecodeInt64()
return cty.Number, err
case code == msgpackcodes.Uint8 || code == msgpackcodes.Uint16 || code == msgpackcodes.Uint32 || code == msgpackcodes.Uint64:
_, err := dec.DecodeUint64()
return cty.Number, err
case code == msgpackcodes.Float || code == msgpackcodes.Double:
_, err := dec.DecodeFloat64()
return cty.Number, err
case msgpackcodes.IsString(code):
_, err := dec.DecodeString()
return cty.String, err
case msgpackcodes.IsFixedMap(code) || code == msgpackcodes.Map16 || code == msgpackcodes.Map32:
return impliedObjectType(dec)
case msgpackcodes.IsFixedArray(code) || code == msgpackcodes.Array16 || code == msgpackcodes.Array32:
return impliedTupleType(dec)
default:
return cty.NilType, fmt.Errorf("unsupported msgpack code %#v", code)
}
}
func impliedObjectType(dec *msgpack.Decoder) (cty.Type, error) {
// If we get in here then we've already peeked the next code and know
// it's some sort of map.
l, err := dec.DecodeMapLen()
if err != nil {
return cty.DynamicPseudoType, nil
}
var atys map[string]cty.Type
for i := 0; i < l; i++ {
// Read the map key first. We require maps to be strings, but msgpack
// doesn't so we're prepared to error here if not.
k, err := dec.DecodeString()
if err != nil {
return cty.DynamicPseudoType, err
}
aty, err := impliedType(dec)
if err != nil {
return cty.DynamicPseudoType, err
}
if atys == nil {
atys = make(map[string]cty.Type)
}
atys[k] = aty
}
if len(atys) == 0 {
return cty.EmptyObject, nil
}
return cty.Object(atys), nil
}
func impliedTupleType(dec *msgpack.Decoder) (cty.Type, error) {
// If we get in here then we've already peeked the next code and know
// it's some sort of array.
l, err := dec.DecodeArrayLen()
if err != nil {
return cty.DynamicPseudoType, nil
}
if l == 0 {
return cty.EmptyTuple, nil
}
etys := make([]cty.Type, l)
for i := 0; i < l; i++ {
ety, err := impliedType(dec)
if err != nil {
return cty.DynamicPseudoType, err
}
etys[i] = ety
}
return cty.Tuple(etys), nil
}

View File

@ -0,0 +1,16 @@
package msgpack
type unknownType struct{}
var unknownVal = unknownType{}
// unknownValBytes is the raw bytes of the msgpack fixext1 value we
// write to represent an unknown value. It's an extension value of
// type zero whose value is irrelevant. Since it's irrelevant, we
// set it to a single byte whose value is also zero, since that's
// the most compact possible representation.
var unknownValBytes = []byte{0xd4, 0, 0}
func (uv unknownType) MarshalMsgpack() ([]byte, error) {
return unknownValBytes, nil
}

View File

@ -0,0 +1,334 @@
package msgpack
import (
"bytes"
"github.com/hashicorp/go-cty/cty"
"github.com/vmihailenco/msgpack"
msgpackCodes "github.com/vmihailenco/msgpack/codes"
)
// Unmarshal interprets the given bytes as a msgpack-encoded cty Value of
// the given type, returning the result.
//
// If an error is returned, the error is written with a hypothetical
// end-user that wrote the msgpack file as its audience, using cty type
// system concepts rather than Go type system concepts.
func Unmarshal(b []byte, ty cty.Type) (cty.Value, error) {
r := bytes.NewReader(b)
dec := msgpack.NewDecoder(r)
var path cty.Path
return unmarshal(dec, ty, path)
}
func unmarshal(dec *msgpack.Decoder, ty cty.Type, path cty.Path) (cty.Value, error) {
peek, err := dec.PeekCode()
if err != nil {
return cty.DynamicVal, path.NewError(err)
}
if msgpackCodes.IsExt(peek) {
// We just assume _all_ extensions are unknown values,
// since we don't have any other extensions.
dec.Skip() // skip what we've peeked
return cty.UnknownVal(ty), nil
}
if ty == cty.DynamicPseudoType {
return unmarshalDynamic(dec, path)
}
if peek == msgpackCodes.Nil {
dec.Skip() // skip what we've peeked
return cty.NullVal(ty), nil
}
switch {
case ty.IsPrimitiveType():
val, err := unmarshalPrimitive(dec, ty, path)
if err != nil {
return cty.NilVal, err
}
return val, nil
case ty.IsListType():
return unmarshalList(dec, ty.ElementType(), path)
case ty.IsSetType():
return unmarshalSet(dec, ty.ElementType(), path)
case ty.IsMapType():
return unmarshalMap(dec, ty.ElementType(), path)
case ty.IsTupleType():
return unmarshalTuple(dec, ty.TupleElementTypes(), path)
case ty.IsObjectType():
return unmarshalObject(dec, ty.AttributeTypes(), path)
default:
return cty.NilVal, path.NewErrorf("unsupported type %s", ty.FriendlyName())
}
}
func unmarshalPrimitive(dec *msgpack.Decoder, ty cty.Type, path cty.Path) (cty.Value, error) {
switch ty {
case cty.Bool:
rv, err := dec.DecodeBool()
if err != nil {
return cty.DynamicVal, path.NewErrorf("bool is required")
}
return cty.BoolVal(rv), nil
case cty.Number:
// Marshal will try int and float first, if the value can be
// losslessly represented in these encodings, and then fall
// back on a string if the number is too large or too precise.
peek, err := dec.PeekCode()
if err != nil {
return cty.DynamicVal, path.NewErrorf("number is required")
}
if msgpackCodes.IsFixedNum(peek) {
rv, err := dec.DecodeInt64()
if err != nil {
return cty.DynamicVal, path.NewErrorf("number is required")
}
return cty.NumberIntVal(rv), nil
}
switch peek {
case msgpackCodes.Int8, msgpackCodes.Int16, msgpackCodes.Int32, msgpackCodes.Int64:
rv, err := dec.DecodeInt64()
if err != nil {
return cty.DynamicVal, path.NewErrorf("number is required")
}
return cty.NumberIntVal(rv), nil
case msgpackCodes.Uint8, msgpackCodes.Uint16, msgpackCodes.Uint32, msgpackCodes.Uint64:
rv, err := dec.DecodeUint64()
if err != nil {
return cty.DynamicVal, path.NewErrorf("number is required")
}
return cty.NumberUIntVal(rv), nil
case msgpackCodes.Float, msgpackCodes.Double:
rv, err := dec.DecodeFloat64()
if err != nil {
return cty.DynamicVal, path.NewErrorf("number is required")
}
return cty.NumberFloatVal(rv), nil
default:
rv, err := dec.DecodeString()
if err != nil {
return cty.DynamicVal, path.NewErrorf("number is required")
}
v, err := cty.ParseNumberVal(rv)
if err != nil {
return cty.DynamicVal, path.NewErrorf("number is required")
}
return v, nil
}
case cty.String:
rv, err := dec.DecodeString()
if err != nil {
return cty.DynamicVal, path.NewErrorf("string is required")
}
return cty.StringVal(rv), nil
default:
// should never happen
panic("unsupported primitive type")
}
}
func unmarshalList(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value, error) {
length, err := dec.DecodeArrayLen()
if err != nil {
return cty.DynamicVal, path.NewErrorf("a list is required")
}
switch {
case length < 0:
return cty.NullVal(cty.List(ety)), nil
case length == 0:
return cty.ListValEmpty(ety), nil
}
vals := make([]cty.Value, 0, length)
path = append(path, nil)
for i := 0; i < length; i++ {
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(int64(i)),
}
val, err := unmarshal(dec, ety, path)
if err != nil {
return cty.DynamicVal, err
}
vals = append(vals, val)
}
return cty.ListVal(vals), nil
}
func unmarshalSet(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value, error) {
length, err := dec.DecodeArrayLen()
if err != nil {
return cty.DynamicVal, path.NewErrorf("a set is required")
}
switch {
case length < 0:
return cty.NullVal(cty.Set(ety)), nil
case length == 0:
return cty.SetValEmpty(ety), nil
}
vals := make([]cty.Value, 0, length)
path = append(path, nil)
for i := 0; i < length; i++ {
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(int64(i)),
}
val, err := unmarshal(dec, ety, path)
if err != nil {
return cty.DynamicVal, err
}
vals = append(vals, val)
}
return cty.SetVal(vals), nil
}
func unmarshalMap(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value, error) {
length, err := dec.DecodeMapLen()
if err != nil {
return cty.DynamicVal, path.NewErrorf("a map is required")
}
switch {
case length < 0:
return cty.NullVal(cty.Map(ety)), nil
case length == 0:
return cty.MapValEmpty(ety), nil
}
vals := make(map[string]cty.Value, length)
path = append(path, nil)
for i := 0; i < length; i++ {
key, err := dec.DecodeString()
if err != nil {
path[:len(path)-1].NewErrorf("non-string key in map")
}
path[len(path)-1] = cty.IndexStep{
Key: cty.StringVal(key),
}
val, err := unmarshal(dec, ety, path)
if err != nil {
return cty.DynamicVal, err
}
vals[key] = val
}
return cty.MapVal(vals), nil
}
func unmarshalTuple(dec *msgpack.Decoder, etys []cty.Type, path cty.Path) (cty.Value, error) {
length, err := dec.DecodeArrayLen()
if err != nil {
return cty.DynamicVal, path.NewErrorf("a tuple is required")
}
switch {
case length < 0:
return cty.NullVal(cty.Tuple(etys)), nil
case length == 0:
return cty.TupleVal(nil), nil
case length != len(etys):
return cty.DynamicVal, path.NewErrorf("a tuple of length %d is required", len(etys))
}
vals := make([]cty.Value, 0, length)
path = append(path, nil)
for i := 0; i < length; i++ {
path[len(path)-1] = cty.IndexStep{
Key: cty.NumberIntVal(int64(i)),
}
ety := etys[i]
val, err := unmarshal(dec, ety, path)
if err != nil {
return cty.DynamicVal, err
}
vals = append(vals, val)
}
return cty.TupleVal(vals), nil
}
func unmarshalObject(dec *msgpack.Decoder, atys map[string]cty.Type, path cty.Path) (cty.Value, error) {
length, err := dec.DecodeMapLen()
if err != nil {
return cty.DynamicVal, path.NewErrorf("an object is required")
}
switch {
case length < 0:
return cty.NullVal(cty.Object(atys)), nil
case length == 0:
return cty.ObjectVal(nil), nil
case length != len(atys):
return cty.DynamicVal, path.NewErrorf("an object with %d attributes is required (%d given)",
len(atys), length)
}
vals := make(map[string]cty.Value, length)
path = append(path, nil)
for i := 0; i < length; i++ {
key, err := dec.DecodeString()
if err != nil {
return cty.DynamicVal, path[:len(path)-1].NewErrorf("all keys must be strings")
}
path[len(path)-1] = cty.IndexStep{
Key: cty.StringVal(key),
}
aty, exists := atys[key]
if !exists {
return cty.DynamicVal, path.NewErrorf("unsupported attribute")
}
val, err := unmarshal(dec, aty, path)
if err != nil {
return cty.DynamicVal, err
}
vals[key] = val
}
return cty.ObjectVal(vals), nil
}
func unmarshalDynamic(dec *msgpack.Decoder, path cty.Path) (cty.Value, error) {
length, err := dec.DecodeArrayLen()
if err != nil {
return cty.DynamicVal, path.NewError(err)
}
switch {
case length == -1:
return cty.NullVal(cty.DynamicPseudoType), nil
case length != 2:
return cty.DynamicVal, path.NewErrorf(
"dynamic value array must have exactly two elements",
)
}
typeJSON, err := dec.DecodeBytes()
if err != nil {
return cty.DynamicVal, path.NewError(err)
}
var ty cty.Type
err = (&ty).UnmarshalJSON(typeJSON)
if err != nil {
return cty.DynamicVal, path.NewError(err)
}
return unmarshal(dec, ty, path)
}

14
vendor/github.com/hashicorp/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/hashicorp/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")
}

270
vendor/github.com/hashicorp/go-cty/cty/path.go generated vendored Normal file
View File

@ -0,0 +1,270 @@
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
}
// IndexInt is a typed convenience method for Index.
func (p Path) IndexInt(v int) Path {
return p.Index(NumberIntVal(int64(v)))
}
// IndexString is a typed convenience method for Index.
func (p Path) IndexString(v string) Path {
return p.Index(StringVal(v))
}
// IndexPath is a convenience method to start a new Path with an IndexStep.
func IndexPath(v Value) Path {
return Path{}.Index(v)
}
// IndexIntPath is a typed convenience method for IndexPath.
func IndexIntPath(v int) Path {
return IndexPath(NumberIntVal(int64(v)))
}
// IndexStringPath is a typed convenience method for IndexPath.
func IndexStringPath(v string) Path {
return IndexPath(StringVal(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/hashicorp/go-cty/cty/path_set.go generated vendored Normal file
View File

@ -0,0 +1,198 @@
package cty
import (
"fmt"
"hash/crc64"
"github.com/hashicorp/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
}

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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/go-cty/cty/set_helper.go generated vendored Normal file
View File

@ -0,0 +1,132 @@
package cty
import (
"fmt"
"github.com/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/go-cty/%s", typeName), tv)
} else {
gob.Register(tv)
}
}
}

84
vendor/github.com/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/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/hashicorp/go-cty/cty/value_ops.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

182
vendor/github.com/hashicorp/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)
}