updated GHA
Update to v2 SDK updated dependencies
This commit is contained in:
21
vendor/github.com/hashicorp/go-cty/LICENSE
generated
vendored
Normal file
21
vendor/github.com/hashicorp/go-cty/LICENSE
generated
vendored
Normal 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
128
vendor/github.com/hashicorp/go-cty/cty/capsule.go
generated
vendored
Normal 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
132
vendor/github.com/hashicorp/go-cty/cty/capsule_ops.go
generated
vendored
Normal 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
34
vendor/github.com/hashicorp/go-cty/cty/collection.go
generated
vendored
Normal 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)
|
165
vendor/github.com/hashicorp/go-cty/cty/convert/compare_types.go
generated
vendored
Normal file
165
vendor/github.com/hashicorp/go-cty/cty/convert/compare_types.go
generated
vendored
Normal 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
|
||||
}
|
190
vendor/github.com/hashicorp/go-cty/cty/convert/conversion.go
generated
vendored
Normal file
190
vendor/github.com/hashicorp/go-cty/cty/convert/conversion.go
generated
vendored
Normal 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))
|
||||
}
|
||||
}
|
31
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_capsule.go
generated
vendored
Normal file
31
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_capsule.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
488
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_collection.go
generated
vendored
Normal file
488
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_collection.go
generated
vendored
Normal 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
|
||||
}
|
33
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_dynamic.go
generated
vendored
Normal file
33
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_dynamic.go
generated
vendored
Normal 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
|
||||
}
|
76
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_object.go
generated
vendored
Normal file
76
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_object.go
generated
vendored
Normal 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
|
||||
}
|
||||
}
|
57
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_primitive.go
generated
vendored
Normal file
57
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_primitive.go
generated
vendored
Normal 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")
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
71
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_tuple.go
generated
vendored
Normal file
71
vendor/github.com/hashicorp/go-cty/cty/convert/conversion_tuple.go
generated
vendored
Normal 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
15
vendor/github.com/hashicorp/go-cty/cty/convert/doc.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// Package convert contains some routines for converting between cty types.
|
||||
// The intent of providing this package is to encourage applications using
|
||||
// cty to have consistent type conversion behavior for maximal interoperability
|
||||
// when Values pass from one application to another.
|
||||
//
|
||||
// The conversions are categorized into two categories. "Safe" conversions are
|
||||
// ones that are guaranteed to succeed if given a non-null value of the
|
||||
// appropriate source type. "Unsafe" conversions, on the other hand, are valid
|
||||
// for only a subset of input values, and thus may fail with an error when
|
||||
// called for values outside of that valid subset.
|
||||
//
|
||||
// The functions whose names end in Unsafe support all of the conversions that
|
||||
// are supported by the corresponding functions whose names do not have that
|
||||
// suffix, and then additional unsafe conversions as well.
|
||||
package convert
|
220
vendor/github.com/hashicorp/go-cty/cty/convert/mismatch_msg.go
generated
vendored
Normal file
220
vendor/github.com/hashicorp/go-cty/cty/convert/mismatch_msg.go
generated
vendored
Normal 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))
|
||||
}
|
83
vendor/github.com/hashicorp/go-cty/cty/convert/public.go
generated
vendored
Normal file
83
vendor/github.com/hashicorp/go-cty/cty/convert/public.go
generated
vendored
Normal 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)
|
||||
}
|
69
vendor/github.com/hashicorp/go-cty/cty/convert/sort_types.go
generated
vendored
Normal file
69
vendor/github.com/hashicorp/go-cty/cty/convert/sort_types.go
generated
vendored
Normal 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
357
vendor/github.com/hashicorp/go-cty/cty/convert/unify.go
generated
vendored
Normal 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
18
vendor/github.com/hashicorp/go-cty/cty/doc.go
generated
vendored
Normal 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
|
194
vendor/github.com/hashicorp/go-cty/cty/element_iterator.go
generated
vendored
Normal file
194
vendor/github.com/hashicorp/go-cty/cty/element_iterator.go
generated
vendored
Normal 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
55
vendor/github.com/hashicorp/go-cty/cty/error.go
generated
vendored
Normal 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
204
vendor/github.com/hashicorp/go-cty/cty/gob.go
generated
vendored
Normal 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(>)
|
||||
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
7
vendor/github.com/hashicorp/go-cty/cty/gocty/doc.go
generated
vendored
Normal 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
|
43
vendor/github.com/hashicorp/go-cty/cty/gocty/helpers.go
generated
vendored
Normal file
43
vendor/github.com/hashicorp/go-cty/cty/gocty/helpers.go
generated
vendored
Normal 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
548
vendor/github.com/hashicorp/go-cty/cty/gocty/in.go
generated
vendored
Normal 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
686
vendor/github.com/hashicorp/go-cty/cty/gocty/out.go
generated
vendored
Normal 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")
|
||||
|
||||
}
|
||||
}
|
108
vendor/github.com/hashicorp/go-cty/cty/gocty/type_implied.go
generated
vendored
Normal file
108
vendor/github.com/hashicorp/go-cty/cty/gocty/type_implied.go
generated
vendored
Normal 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
99
vendor/github.com/hashicorp/go-cty/cty/helper.go
generated
vendored
Normal 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
176
vendor/github.com/hashicorp/go-cty/cty/json.go
generated
vendored
Normal 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
11
vendor/github.com/hashicorp/go-cty/cty/json/doc.go
generated
vendored
Normal 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
193
vendor/github.com/hashicorp/go-cty/cty/json/marshal.go
generated
vendored
Normal 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
41
vendor/github.com/hashicorp/go-cty/cty/json/simple.go
generated
vendored
Normal 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
23
vendor/github.com/hashicorp/go-cty/cty/json/type.go
generated
vendored
Normal 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
|
||||
}
|
170
vendor/github.com/hashicorp/go-cty/cty/json/type_implied.go
generated
vendored
Normal file
170
vendor/github.com/hashicorp/go-cty/cty/json/type_implied.go
generated
vendored
Normal 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
|
||||
}
|
459
vendor/github.com/hashicorp/go-cty/cty/json/unmarshal.go
generated
vendored
Normal file
459
vendor/github.com/hashicorp/go-cty/cty/json/unmarshal.go
generated
vendored
Normal 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
65
vendor/github.com/hashicorp/go-cty/cty/json/value.go
generated
vendored
Normal 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
74
vendor/github.com/hashicorp/go-cty/cty/list_type.go
generated
vendored
Normal 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 <.ElementTypeT
|
||||
}
|
||||
return nil
|
||||
}
|
74
vendor/github.com/hashicorp/go-cty/cty/map_type.go
generated
vendored
Normal file
74
vendor/github.com/hashicorp/go-cty/cty/map_type.go
generated
vendored
Normal 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 <.ElementTypeT
|
||||
}
|
||||
return nil
|
||||
}
|
296
vendor/github.com/hashicorp/go-cty/cty/marks.go
generated
vendored
Normal file
296
vendor/github.com/hashicorp/go-cty/cty/marks.go
generated
vendored
Normal 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
14
vendor/github.com/hashicorp/go-cty/cty/msgpack/doc.go
generated
vendored
Normal 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
|
31
vendor/github.com/hashicorp/go-cty/cty/msgpack/dynamic.go
generated
vendored
Normal file
31
vendor/github.com/hashicorp/go-cty/cty/msgpack/dynamic.go
generated
vendored
Normal 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
|
||||
}
|
8
vendor/github.com/hashicorp/go-cty/cty/msgpack/infinity.go
generated
vendored
Normal file
8
vendor/github.com/hashicorp/go-cty/cty/msgpack/infinity.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package msgpack
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
var negativeInfinity = math.Inf(-1)
|
||||
var positiveInfinity = math.Inf(1)
|
211
vendor/github.com/hashicorp/go-cty/cty/msgpack/marshal.go
generated
vendored
Normal file
211
vendor/github.com/hashicorp/go-cty/cty/msgpack/marshal.go
generated
vendored
Normal 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)
|
||||
}
|
167
vendor/github.com/hashicorp/go-cty/cty/msgpack/type_implied.go
generated
vendored
Normal file
167
vendor/github.com/hashicorp/go-cty/cty/msgpack/type_implied.go
generated
vendored
Normal 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
|
||||
}
|
16
vendor/github.com/hashicorp/go-cty/cty/msgpack/unknown.go
generated
vendored
Normal file
16
vendor/github.com/hashicorp/go-cty/cty/msgpack/unknown.go
generated
vendored
Normal 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
|
||||
}
|
334
vendor/github.com/hashicorp/go-cty/cty/msgpack/unmarshal.go
generated
vendored
Normal file
334
vendor/github.com/hashicorp/go-cty/cty/msgpack/unmarshal.go
generated
vendored
Normal 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
14
vendor/github.com/hashicorp/go-cty/cty/null.go
generated
vendored
Normal 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
135
vendor/github.com/hashicorp/go-cty/cty/object_type.go
generated
vendored
Normal 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
270
vendor/github.com/hashicorp/go-cty/cty/path.go
generated
vendored
Normal 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
198
vendor/github.com/hashicorp/go-cty/cty/path_set.go
generated
vendored
Normal 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
|
||||
}
|
122
vendor/github.com/hashicorp/go-cty/cty/primitive_type.go
generated
vendored
Normal file
122
vendor/github.com/hashicorp/go-cty/cty/primitive_type.go
generated
vendored
Normal 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
76
vendor/github.com/hashicorp/go-cty/cty/set/gob.go
generated
vendored
Normal 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
15
vendor/github.com/hashicorp/go-cty/cty/set/iterator.go
generated
vendored
Normal 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
210
vendor/github.com/hashicorp/go-cty/cty/set/ops.go
generated
vendored
Normal 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
43
vendor/github.com/hashicorp/go-cty/cty/set/rules.go
generated
vendored
Normal 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
62
vendor/github.com/hashicorp/go-cty/cty/set/set.go
generated
vendored
Normal 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
132
vendor/github.com/hashicorp/go-cty/cty/set_helper.go
generated
vendored
Normal 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
244
vendor/github.com/hashicorp/go-cty/cty/set_internals.go
generated
vendored
Normal 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
72
vendor/github.com/hashicorp/go-cty/cty/set_type.go
generated
vendored
Normal 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 <.ElementTypeT
|
||||
}
|
||||
return nil
|
||||
}
|
121
vendor/github.com/hashicorp/go-cty/cty/tuple_type.go
generated
vendored
Normal file
121
vendor/github.com/hashicorp/go-cty/cty/tuple_type.go
generated
vendored
Normal 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
120
vendor/github.com/hashicorp/go-cty/cty/type.go
generated
vendored
Normal 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
139
vendor/github.com/hashicorp/go-cty/cty/type_conform.go
generated
vendored
Normal 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()),
|
||||
)
|
||||
}
|
57
vendor/github.com/hashicorp/go-cty/cty/types_to_register.go
generated
vendored
Normal file
57
vendor/github.com/hashicorp/go-cty/cty/types_to_register.go
generated
vendored
Normal 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
84
vendor/github.com/hashicorp/go-cty/cty/unknown.go
generated
vendored
Normal 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,
|
||||
}
|
||||
}
|
64
vendor/github.com/hashicorp/go-cty/cty/unknown_as_null.go
generated
vendored
Normal file
64
vendor/github.com/hashicorp/go-cty/cty/unknown_as_null.go
generated
vendored
Normal 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
108
vendor/github.com/hashicorp/go-cty/cty/value.go
generated
vendored
Normal 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
324
vendor/github.com/hashicorp/go-cty/cty/value_init.go
generated
vendored
Normal 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
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
182
vendor/github.com/hashicorp/go-cty/cty/walk.go
generated
vendored
Normal 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)
|
||||
}
|
Reference in New Issue
Block a user