add vendor
This commit is contained in:
21
vendor/github.com/zclconf/go-cty/LICENSE
generated
vendored
Normal file
21
vendor/github.com/zclconf/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/zclconf/go-cty/cty/capsule.go
generated
vendored
Normal file
128
vendor/github.com/zclconf/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/zclconf/go-cty/cty/capsule_ops.go
generated
vendored
Normal file
132
vendor/github.com/zclconf/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/zclconf/go-cty/cty/collection.go
generated
vendored
Normal file
34
vendor/github.com/zclconf/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/zclconf/go-cty/cty/convert/compare_types.go
generated
vendored
Normal file
165
vendor/github.com/zclconf/go-cty/cty/convert/compare_types.go
generated
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// compareTypes implements a preference order for unification.
|
||||
//
|
||||
// The result of this method is not useful for anything other than unification
|
||||
// preferences, since it assumes that the caller will verify that any suggested
|
||||
// conversion is actually possible and it is thus able to to make certain
|
||||
// optimistic assumptions.
|
||||
func compareTypes(a cty.Type, b cty.Type) int {
|
||||
|
||||
// DynamicPseudoType always has lowest preference, because anything can
|
||||
// convert to it (it acts as a placeholder for "any type") and we want
|
||||
// to optimistically assume that any dynamics will converge on matching
|
||||
// their neighbors.
|
||||
if a == cty.DynamicPseudoType || b == cty.DynamicPseudoType {
|
||||
if a != cty.DynamicPseudoType {
|
||||
return -1
|
||||
}
|
||||
if b != cty.DynamicPseudoType {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
if a.IsPrimitiveType() && b.IsPrimitiveType() {
|
||||
// String is a supertype of all primitive types, because we can
|
||||
// represent all primitive values as specially-formatted strings.
|
||||
if a == cty.String || b == cty.String {
|
||||
if a != cty.String {
|
||||
return 1
|
||||
}
|
||||
if b != cty.String {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
if a.IsListType() && b.IsListType() {
|
||||
return compareTypes(a.ElementType(), b.ElementType())
|
||||
}
|
||||
if a.IsSetType() && b.IsSetType() {
|
||||
return compareTypes(a.ElementType(), b.ElementType())
|
||||
}
|
||||
if a.IsMapType() && b.IsMapType() {
|
||||
return compareTypes(a.ElementType(), b.ElementType())
|
||||
}
|
||||
|
||||
// From this point on we may have swapped the two items in order to
|
||||
// simplify our cases. Therefore any non-zero return after this point
|
||||
// must be multiplied by "swap" to potentially invert the return value
|
||||
// if needed.
|
||||
swap := 1
|
||||
switch {
|
||||
case a.IsTupleType() && b.IsListType():
|
||||
fallthrough
|
||||
case a.IsObjectType() && b.IsMapType():
|
||||
fallthrough
|
||||
case a.IsSetType() && b.IsTupleType():
|
||||
fallthrough
|
||||
case a.IsSetType() && b.IsListType():
|
||||
a, b = b, a
|
||||
swap = -1
|
||||
}
|
||||
|
||||
if b.IsSetType() && (a.IsTupleType() || a.IsListType()) {
|
||||
// We'll just optimistically assume that the element types are
|
||||
// unifyable/convertible, and let a second recursive pass
|
||||
// figure out how to make that so.
|
||||
return -1 * swap
|
||||
}
|
||||
|
||||
if a.IsListType() && b.IsTupleType() {
|
||||
// We'll just optimistically assume that the tuple's element types
|
||||
// can be unified into something compatible with the list's element
|
||||
// type.
|
||||
return -1 * swap
|
||||
}
|
||||
|
||||
if a.IsMapType() && b.IsObjectType() {
|
||||
// We'll just optimistically assume that the object's attribute types
|
||||
// can be unified into something compatible with the map's element
|
||||
// type.
|
||||
return -1 * swap
|
||||
}
|
||||
|
||||
// For object and tuple types, comparing two types doesn't really tell
|
||||
// the whole story because it may be possible to construct a new type C
|
||||
// that is the supertype of both A and B by unifying each attribute/element
|
||||
// separately. That possibility is handled by Unify as a follow-up if
|
||||
// type sorting is insufficient to produce a valid result.
|
||||
//
|
||||
// Here we will take care of the simple possibilities where no new type
|
||||
// is needed.
|
||||
if a.IsObjectType() && b.IsObjectType() {
|
||||
atysA := a.AttributeTypes()
|
||||
atysB := b.AttributeTypes()
|
||||
|
||||
if len(atysA) != len(atysB) {
|
||||
return 0
|
||||
}
|
||||
|
||||
hasASuper := false
|
||||
hasBSuper := false
|
||||
for k := range atysA {
|
||||
if _, has := atysB[k]; !has {
|
||||
return 0
|
||||
}
|
||||
|
||||
cmp := compareTypes(atysA[k], atysB[k])
|
||||
if cmp < 0 {
|
||||
hasASuper = true
|
||||
} else if cmp > 0 {
|
||||
hasBSuper = true
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case hasASuper && hasBSuper:
|
||||
return 0
|
||||
case hasASuper:
|
||||
return -1 * swap
|
||||
case hasBSuper:
|
||||
return 1 * swap
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
if a.IsTupleType() && b.IsTupleType() {
|
||||
etysA := a.TupleElementTypes()
|
||||
etysB := b.TupleElementTypes()
|
||||
|
||||
if len(etysA) != len(etysB) {
|
||||
return 0
|
||||
}
|
||||
|
||||
hasASuper := false
|
||||
hasBSuper := false
|
||||
for i := range etysA {
|
||||
cmp := compareTypes(etysA[i], etysB[i])
|
||||
if cmp < 0 {
|
||||
hasASuper = true
|
||||
} else if cmp > 0 {
|
||||
hasBSuper = true
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case hasASuper && hasBSuper:
|
||||
return 0
|
||||
case hasASuper:
|
||||
return -1 * swap
|
||||
case hasBSuper:
|
||||
return 1 * swap
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
201
vendor/github.com/zclconf/go-cty/cty/convert/conversion.go
generated
vendored
Normal file
201
vendor/github.com/zclconf/go-cty/cty/convert/conversion.go
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// conversion is an internal variant of Conversion that carries around
|
||||
// a cty.Path to be used in error responses.
|
||||
type conversion func(cty.Value, cty.Path) (cty.Value, error)
|
||||
|
||||
func getConversion(in cty.Type, out cty.Type, unsafe bool) conversion {
|
||||
conv := getConversionKnown(in, out, unsafe)
|
||||
if conv == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wrap the conversion in some standard checks that we don't want to
|
||||
// have to repeat in every conversion function.
|
||||
var ret conversion
|
||||
ret = func(in cty.Value, path cty.Path) (cty.Value, error) {
|
||||
if in.IsMarked() {
|
||||
// We must unmark during the conversion and then re-apply the
|
||||
// same marks to the result.
|
||||
in, inMarks := in.Unmark()
|
||||
v, err := ret(in, path)
|
||||
if v != cty.NilVal {
|
||||
v = v.WithMarks(inMarks)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
if out == cty.DynamicPseudoType {
|
||||
// Conversion to DynamicPseudoType always just passes through verbatim.
|
||||
return in, nil
|
||||
}
|
||||
if isKnown, isNull := in.IsKnown(), in.IsNull(); !isKnown || isNull {
|
||||
// Avoid constructing unknown or null values with types which
|
||||
// include optional attributes. Known or non-null object values
|
||||
// will be passed to a conversion function which drops the optional
|
||||
// attributes from the type. Unknown and null pass through values
|
||||
// must do the same to ensure that homogeneous collections have a
|
||||
// single element type.
|
||||
out = out.WithoutOptionalAttributesDeep()
|
||||
|
||||
if !isKnown {
|
||||
return cty.UnknownVal(out), nil
|
||||
}
|
||||
|
||||
if 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/zclconf/go-cty/cty/convert/conversion_capsule.go
generated
vendored
Normal file
31
vendor/github.com/zclconf/go-cty/cty/convert/conversion_capsule.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func conversionToCapsule(inTy, outTy cty.Type, fn func(inTy cty.Type) func(cty.Value, cty.Path) (interface{}, error)) conversion {
|
||||
rawConv := fn(inTy)
|
||||
if rawConv == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return func(in cty.Value, path cty.Path) (cty.Value, error) {
|
||||
rawV, err := rawConv(in, path)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
return cty.CapsuleVal(outTy, rawV), nil
|
||||
}
|
||||
}
|
||||
|
||||
func conversionFromCapsule(inTy, outTy cty.Type, fn func(outTy cty.Type) func(interface{}, cty.Path) (cty.Value, error)) conversion {
|
||||
rawConv := fn(outTy)
|
||||
if rawConv == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return func(in cty.Value, path cty.Path) (cty.Value, error) {
|
||||
return rawConv(in.EncapsulatedValue(), path)
|
||||
}
|
||||
}
|
568
vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go
generated
vendored
Normal file
568
vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go
generated
vendored
Normal file
@ -0,0 +1,568 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// conversionCollectionToList returns a conversion that will apply the given
|
||||
// conversion to all of the elements of a collection (something that supports
|
||||
// ForEachElement and LengthInt) and then returns the result as a list.
|
||||
//
|
||||
// "conv" can be nil if the elements are expected to already be of the
|
||||
// correct type and just need to be re-wrapped into a list. (For example,
|
||||
// if we're converting from a set into a list of the same element type.)
|
||||
func conversionCollectionToList(ety cty.Type, conv conversion) conversion {
|
||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
if !val.Length().IsKnown() {
|
||||
// If the input collection has an unknown length (which is true
|
||||
// for a set containing unknown values) then our result must be
|
||||
// an unknown list, because we can't predict how many elements
|
||||
// the resulting list should have.
|
||||
return cty.UnknownVal(cty.List(val.Type().ElementType())), nil
|
||||
}
|
||||
|
||||
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 list
|
||||
if ety == cty.DynamicPseudoType {
|
||||
return cty.ListValEmpty(val.Type().ElementType()), nil
|
||||
}
|
||||
return cty.ListValEmpty(ety), nil
|
||||
}
|
||||
|
||||
if !cty.CanListVal(elems) {
|
||||
return cty.NilVal, path.NewErrorf("element types must all match for conversion to list")
|
||||
}
|
||||
|
||||
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 {
|
||||
return cty.SetValEmpty(val.Type().ElementType()), nil
|
||||
}
|
||||
return cty.SetValEmpty(ety), nil
|
||||
}
|
||||
|
||||
if !cty.CanSetVal(elems) {
|
||||
return cty.NilVal, path.NewErrorf("element types must all match for conversion to set")
|
||||
}
|
||||
|
||||
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 {
|
||||
return cty.MapValEmpty(val.Type().ElementType()), nil
|
||||
}
|
||||
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 !cty.CanMapVal(elems) {
|
||||
return cty.NilVal, path.NewErrorf("element types must all match for conversion to map")
|
||||
}
|
||||
|
||||
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, setEty 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(setEty), nil
|
||||
}
|
||||
}
|
||||
|
||||
if setEty == 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.
|
||||
setEty, _ = unify(tupleEtys, unsafe)
|
||||
if setEty == cty.NilType {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the set element type after unification is still the dynamic
|
||||
// type, the only way this can result in a valid set is if all values
|
||||
// are of dynamic type
|
||||
if setEty == cty.DynamicPseudoType {
|
||||
for _, tupleEty := range tupleEtys {
|
||||
if !tupleEty.Equals(cty.DynamicPseudoType) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elemConvs := make([]conversion, len(tupleEtys))
|
||||
for i, tupleEty := range tupleEtys {
|
||||
if tupleEty.Equals(setEty) {
|
||||
// no conversion required
|
||||
continue
|
||||
}
|
||||
|
||||
elemConvs[i] = getConversion(tupleEty, setEty, 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++
|
||||
}
|
||||
|
||||
if !cty.CanSetVal(elems) {
|
||||
return cty.NilVal, path.NewErrorf("element types must all match for conversion to set")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// If the list element type after unification is still the dynamic
|
||||
// type, the only way this can result in a valid list is if all values
|
||||
// are of dynamic type
|
||||
if listEty == cty.DynamicPseudoType {
|
||||
for _, tupleEty := range tupleEtys {
|
||||
if !tupleEty.Equals(cty.DynamicPseudoType) {
|
||||
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))
|
||||
elemTys := make([]cty.Type, 0, len(elems))
|
||||
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)
|
||||
elemTys = append(elemTys, val.Type())
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
elems, err := conversionUnifyListElements(elems, elemPath, unsafe)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
|
||||
if !cty.CanListVal(elems) {
|
||||
return cty.NilVal, path.NewErrorf("element types must all match for conversion to list")
|
||||
}
|
||||
|
||||
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 !cty.CanMapVal(elems) {
|
||||
return cty.NilVal, path.NewErrorf("attribute types must all match for conversion to map")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
for name, aty := range objectAtys {
|
||||
if _, exists := elems[name]; !exists {
|
||||
if optional := objType.AttributeOptional(name); optional {
|
||||
elems[name] = cty.NullVal(aty)
|
||||
} else {
|
||||
return cty.NilVal, path.NewErrorf("map has no element for required attribute %q", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, path.NewErrorf("cannot find a common base type for all elements")
|
||||
}
|
||||
|
||||
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 conversionUnifyListElements(elems []cty.Value, path cty.Path, unsafe bool) ([]cty.Value, error) {
|
||||
elemTypes := make([]cty.Type, len(elems))
|
||||
for i, elem := range elems {
|
||||
elemTypes[i] = elem.Type()
|
||||
}
|
||||
unifiedType, _ := unify(elemTypes, unsafe)
|
||||
if unifiedType == cty.NilType {
|
||||
return nil, path.NewErrorf("cannot find a common base type for all elements")
|
||||
}
|
||||
|
||||
ret := make([]cty.Value, len(elems))
|
||||
elemPath := append(path.Copy(), nil)
|
||||
|
||||
for i, elem := range elems {
|
||||
if elem.Type().Equals(unifiedType) {
|
||||
ret[i] = elem
|
||||
continue
|
||||
}
|
||||
conv := getConversion(elem.Type(), unifiedType, unsafe)
|
||||
if conv == nil {
|
||||
}
|
||||
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||
Key: cty.NumberIntVal(int64(i)),
|
||||
}
|
||||
val, err := conv(elem, elemPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[i] = val
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
33
vendor/github.com/zclconf/go-cty/cty/convert/conversion_dynamic.go
generated
vendored
Normal file
33
vendor/github.com/zclconf/go-cty/cty/convert/conversion_dynamic.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// dynamicFixup deals with just-in-time conversions of values that were
|
||||
// input-typed as cty.DynamicPseudoType during analysis, ensuring that
|
||||
// we end up with the desired output type once the value is known, or
|
||||
// failing with an error if that is not possible.
|
||||
//
|
||||
// This is in the spirit of the cty philosophy of optimistically assuming that
|
||||
// DynamicPseudoType values will become the intended value eventually, and
|
||||
// dealing with any inconsistencies during final evaluation.
|
||||
func dynamicFixup(wantType cty.Type) conversion {
|
||||
return func(in cty.Value, path cty.Path) (cty.Value, error) {
|
||||
ret, err := Convert(in, wantType)
|
||||
if err != nil {
|
||||
// Re-wrap this error so that the returned path is relative
|
||||
// to the caller's original value, rather than relative to our
|
||||
// conversion value here.
|
||||
return cty.NilVal, path.NewError(err)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
// dynamicPassthrough is an identity conversion that is used when the
|
||||
// target type is DynamicPseudoType, indicating that the caller doesn't care
|
||||
// which type is returned.
|
||||
func dynamicPassthrough(in cty.Value, path cty.Path) (cty.Value, error) {
|
||||
return in, nil
|
||||
}
|
95
vendor/github.com/zclconf/go-cty/cty/convert/conversion_object.go
generated
vendored
Normal file
95
vendor/github.com/zclconf/go-cty/cty/convert/conversion_object.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// conversionObjectToObject returns a conversion that will make the input
|
||||
// object type conform to the output object type, if possible.
|
||||
//
|
||||
// Conversion is possible only if the output type is a subset of the input
|
||||
// type, meaning that each attribute of the output type has a corresponding
|
||||
// attribute in the input type where a recursive conversion is available.
|
||||
//
|
||||
// If the "out" type has any optional attributes, those attributes may be
|
||||
// absent in the "in" type, in which case null values will be used in their
|
||||
// place in the result.
|
||||
//
|
||||
// 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()
|
||||
outOptionals := out.OptionalAttributes()
|
||||
attrConvs := make(map[string]conversion)
|
||||
|
||||
for name, outAty := range outAtys {
|
||||
inAty, exists := inAtys[name]
|
||||
if !exists {
|
||||
if _, optional := outOptionals[name]; optional {
|
||||
// If it's optional then we'll skip inserting an
|
||||
// attribute conversion and then deal with inserting
|
||||
// the default value in our overall conversion logic
|
||||
// later.
|
||||
continue
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
for name := range outOptionals {
|
||||
if _, exists := attrVals[name]; !exists {
|
||||
wantTy := outAtys[name]
|
||||
attrVals[name] = cty.NullVal(wantTy)
|
||||
}
|
||||
}
|
||||
|
||||
return cty.ObjectVal(attrVals), nil
|
||||
}
|
||||
}
|
57
vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go
generated
vendored
Normal file
57
vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
var stringTrue = cty.StringVal("true")
|
||||
var stringFalse = cty.StringVal("false")
|
||||
|
||||
var primitiveConversionsSafe = map[cty.Type]map[cty.Type]conversion{
|
||||
cty.Number: {
|
||||
cty.String: func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
f := val.AsBigFloat()
|
||||
return cty.StringVal(f.Text('f', -1)), nil
|
||||
},
|
||||
},
|
||||
cty.Bool: {
|
||||
cty.String: func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
if val.True() {
|
||||
return stringTrue, nil
|
||||
} else {
|
||||
return stringFalse, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var primitiveConversionsUnsafe = map[cty.Type]map[cty.Type]conversion{
|
||||
cty.String: {
|
||||
cty.Number: func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
v, err := cty.ParseNumberVal(val.AsString())
|
||||
if err != nil {
|
||||
return cty.NilVal, path.NewErrorf("a number is required")
|
||||
}
|
||||
return v, nil
|
||||
},
|
||||
cty.Bool: func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
switch val.AsString() {
|
||||
case "true", "1":
|
||||
return cty.True, nil
|
||||
case "false", "0":
|
||||
return cty.False, nil
|
||||
default:
|
||||
switch strings.ToLower(val.AsString()) {
|
||||
case "true":
|
||||
return cty.NilVal, path.NewErrorf("a bool is required; to convert from string, use lowercase \"true\"")
|
||||
case "false":
|
||||
return cty.NilVal, path.NewErrorf("a bool is required; to convert from string, use lowercase \"false\"")
|
||||
default:
|
||||
return cty.NilVal, path.NewErrorf("a bool is required")
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
71
vendor/github.com/zclconf/go-cty/cty/convert/conversion_tuple.go
generated
vendored
Normal file
71
vendor/github.com/zclconf/go-cty/cty/convert/conversion_tuple.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// conversionTupleToTuple returns a conversion that will make the input
|
||||
// tuple type conform to the output tuple type, if possible.
|
||||
//
|
||||
// Conversion is possible only if the two tuple types have the same number
|
||||
// of elements and the corresponding elements by index can be converted.
|
||||
//
|
||||
// Shallow tuple conversions work the same for both safe and unsafe modes,
|
||||
// but the safety flag is passed on to recursive conversions and may thus
|
||||
// limit which element type conversions are possible.
|
||||
func conversionTupleToTuple(in, out cty.Type, unsafe bool) conversion {
|
||||
inEtys := in.TupleElementTypes()
|
||||
outEtys := out.TupleElementTypes()
|
||||
|
||||
if len(inEtys) != len(outEtys) {
|
||||
return nil // no conversion is possible
|
||||
}
|
||||
|
||||
elemConvs := make([]conversion, len(inEtys))
|
||||
|
||||
for i, outEty := range outEtys {
|
||||
inEty := inEtys[i]
|
||||
|
||||
if inEty.Equals(outEty) {
|
||||
// No conversion needed, so we can leave this one nil.
|
||||
continue
|
||||
}
|
||||
|
||||
elemConvs[i] = getConversion(inEty, outEty, unsafe)
|
||||
if elemConvs[i] == nil {
|
||||
// If a recursive conversion isn't available, then our top-level
|
||||
// configuration is impossible too.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here then a conversion is possible, using the element
|
||||
// conversions given in elemConvs.
|
||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
elemVals := make([]cty.Value, len(elemConvs))
|
||||
path = append(path, nil)
|
||||
pathStep := &path[len(path)-1]
|
||||
|
||||
i := 0
|
||||
for it := val.ElementIterator(); it.Next(); i++ {
|
||||
_, val := it.Element()
|
||||
var err error
|
||||
|
||||
*pathStep = cty.IndexStep{
|
||||
Key: cty.NumberIntVal(int64(i)),
|
||||
}
|
||||
|
||||
conv := elemConvs[i]
|
||||
if conv != nil {
|
||||
val, err = conv(val, path)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
}
|
||||
|
||||
elemVals[i] = val
|
||||
}
|
||||
|
||||
return cty.TupleVal(elemVals), nil
|
||||
}
|
||||
}
|
15
vendor/github.com/zclconf/go-cty/cty/convert/doc.go
generated
vendored
Normal file
15
vendor/github.com/zclconf/go-cty/cty/convert/doc.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// Package convert contains some routines for converting between cty types.
|
||||
// The intent of providing this package is to encourage applications using
|
||||
// cty to have consistent type conversion behavior for maximal interoperability
|
||||
// when Values pass from one application to another.
|
||||
//
|
||||
// The conversions are categorized into two categories. "Safe" conversions are
|
||||
// ones that are guaranteed to succeed if given a non-null value of the
|
||||
// appropriate source type. "Unsafe" conversions, on the other hand, are valid
|
||||
// for only a subset of input values, and thus may fail with an error when
|
||||
// called for values outside of that valid subset.
|
||||
//
|
||||
// The functions whose names end in Unsafe support all of the conversions that
|
||||
// are supported by the corresponding functions whose names do not have that
|
||||
// suffix, and then additional unsafe conversions as well.
|
||||
package convert
|
226
vendor/github.com/zclconf/go-cty/cty/convert/mismatch_msg.go
generated
vendored
Normal file
226
vendor/github.com/zclconf/go-cty/cty/convert/mismatch_msg.go
generated
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// MismatchMessage is a helper to return an English-language description of
|
||||
// the differences between got and want, phrased as a reason why got does
|
||||
// not conform to want.
|
||||
//
|
||||
// This function does not itself attempt conversion, and so it should generally
|
||||
// be used only after a conversion has failed, to report the conversion failure
|
||||
// to an English-speaking user. The result will be confusing got is actually
|
||||
// conforming to or convertable to want.
|
||||
//
|
||||
// The shorthand helper function Convert uses this function internally to
|
||||
// produce its error messages, so callers of that function do not need to
|
||||
// also use MismatchMessage.
|
||||
//
|
||||
// This function is similar to Type.TestConformance, but it is tailored to
|
||||
// describing conversion failures and so the messages it generates relate
|
||||
// specifically to the conversion rules implemented in this package.
|
||||
func MismatchMessage(got, want cty.Type) string {
|
||||
switch {
|
||||
|
||||
case got.IsObjectType() && want.IsObjectType():
|
||||
// If both types are object types then we may be able to say something
|
||||
// about their respective attributes.
|
||||
return mismatchMessageObjects(got, want)
|
||||
|
||||
case got.IsTupleType() && want.IsListType() && want.ElementType() == cty.DynamicPseudoType:
|
||||
// If conversion from tuple to list failed then it's because we couldn't
|
||||
// find a common type to convert all of the tuple elements to.
|
||||
return "all list elements must have the same type"
|
||||
|
||||
case got.IsTupleType() && want.IsSetType() && want.ElementType() == cty.DynamicPseudoType:
|
||||
// If conversion from tuple to set failed then it's because we couldn't
|
||||
// find a common type to convert all of the tuple elements to.
|
||||
return "all set elements must have the same type"
|
||||
|
||||
case got.IsObjectType() && want.IsMapType() && want.ElementType() == cty.DynamicPseudoType:
|
||||
// If conversion from object to map failed then it's because we couldn't
|
||||
// find a common type to convert all of the object attributes to.
|
||||
return "all map elements must have the same type"
|
||||
|
||||
case (got.IsTupleType() || got.IsObjectType()) && want.IsCollectionType():
|
||||
return mismatchMessageCollectionsFromStructural(got, want)
|
||||
|
||||
case got.IsCollectionType() && want.IsCollectionType():
|
||||
return mismatchMessageCollectionsFromCollections(got, want)
|
||||
|
||||
default:
|
||||
// If we have nothing better to say, we'll just state what was required.
|
||||
return want.FriendlyNameForConstraint() + " required"
|
||||
}
|
||||
}
|
||||
|
||||
func mismatchMessageObjects(got, want cty.Type) string {
|
||||
// Per our conversion rules, "got" is allowed to be a superset of "want",
|
||||
// and so we'll produce error messages here under that assumption.
|
||||
gotAtys := got.AttributeTypes()
|
||||
wantAtys := want.AttributeTypes()
|
||||
|
||||
// If we find missing attributes then we'll report those in preference,
|
||||
// but if not then we will report a maximum of one non-conforming
|
||||
// attribute, just to keep our messages relatively terse.
|
||||
// We'll also prefer to report a recursive type error from an _unsafe_
|
||||
// conversion over a safe one, because these are subjectively more
|
||||
// "serious".
|
||||
var missingAttrs []string
|
||||
var unsafeMismatchAttr string
|
||||
var safeMismatchAttr string
|
||||
|
||||
for name, wantAty := range wantAtys {
|
||||
gotAty, exists := gotAtys[name]
|
||||
if !exists {
|
||||
if !want.AttributeOptional(name) {
|
||||
missingAttrs = append(missingAttrs, name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if gotAty.Equals(wantAty) {
|
||||
continue // exact match, so no problem
|
||||
}
|
||||
|
||||
// We'll now try to convert these attributes in isolation and
|
||||
// see if we have a nested conversion error to report.
|
||||
// We'll try an unsafe conversion first, and then fall back on
|
||||
// safe if unsafe is possible.
|
||||
|
||||
// If we already have an unsafe mismatch attr error then we won't bother
|
||||
// hunting for another one.
|
||||
if unsafeMismatchAttr != "" {
|
||||
continue
|
||||
}
|
||||
if conv := GetConversionUnsafe(gotAty, wantAty); conv == nil {
|
||||
unsafeMismatchAttr = fmt.Sprintf("attribute %q: %s", name, MismatchMessage(gotAty, wantAty))
|
||||
}
|
||||
|
||||
// If we already have a safe mismatch attr error then we won't bother
|
||||
// hunting for another one.
|
||||
if safeMismatchAttr != "" {
|
||||
continue
|
||||
}
|
||||
if conv := GetConversion(gotAty, wantAty); conv == nil {
|
||||
safeMismatchAttr = fmt.Sprintf("attribute %q: %s", name, MismatchMessage(gotAty, wantAty))
|
||||
}
|
||||
}
|
||||
|
||||
// We should now have collected at least one problem. If we have more than
|
||||
// one then we'll use our preference order to decide what is most important
|
||||
// to report.
|
||||
switch {
|
||||
|
||||
case len(missingAttrs) != 0:
|
||||
sort.Strings(missingAttrs)
|
||||
switch len(missingAttrs) {
|
||||
case 1:
|
||||
return fmt.Sprintf("attribute %q is required", missingAttrs[0])
|
||||
case 2:
|
||||
return fmt.Sprintf("attributes %q and %q are required", missingAttrs[0], missingAttrs[1])
|
||||
default:
|
||||
sort.Strings(missingAttrs)
|
||||
var buf bytes.Buffer
|
||||
for _, name := range missingAttrs[:len(missingAttrs)-1] {
|
||||
fmt.Fprintf(&buf, "%q, ", name)
|
||||
}
|
||||
fmt.Fprintf(&buf, "and %q", missingAttrs[len(missingAttrs)-1])
|
||||
return fmt.Sprintf("attributes %s are required", buf.Bytes())
|
||||
}
|
||||
|
||||
case unsafeMismatchAttr != "":
|
||||
return unsafeMismatchAttr
|
||||
|
||||
case safeMismatchAttr != "":
|
||||
return safeMismatchAttr
|
||||
|
||||
default:
|
||||
// We should never get here, but if we do then we'll return
|
||||
// just a generic message.
|
||||
return "incorrect object attributes"
|
||||
}
|
||||
}
|
||||
|
||||
func mismatchMessageCollectionsFromStructural(got, want cty.Type) string {
|
||||
// First some straightforward cases where the kind is just altogether wrong.
|
||||
switch {
|
||||
case want.IsListType() && !got.IsTupleType():
|
||||
return want.FriendlyNameForConstraint() + " required"
|
||||
case want.IsSetType() && !got.IsTupleType():
|
||||
return want.FriendlyNameForConstraint() + " required"
|
||||
case want.IsMapType() && !got.IsObjectType():
|
||||
return want.FriendlyNameForConstraint() + " required"
|
||||
}
|
||||
|
||||
// If the kinds are matched well enough then we'll move on to checking
|
||||
// individual elements.
|
||||
wantEty := want.ElementType()
|
||||
switch {
|
||||
case got.IsTupleType():
|
||||
for i, gotEty := range got.TupleElementTypes() {
|
||||
if gotEty.Equals(wantEty) {
|
||||
continue // exact match, so no problem
|
||||
}
|
||||
if conv := getConversion(gotEty, wantEty, true); conv != nil {
|
||||
continue // conversion is available, so no problem
|
||||
}
|
||||
return fmt.Sprintf("element %d: %s", i, MismatchMessage(gotEty, wantEty))
|
||||
}
|
||||
|
||||
// If we get down here then something weird is going on but we'll
|
||||
// return a reasonable fallback message anyway.
|
||||
return fmt.Sprintf("all elements must be %s", wantEty.FriendlyNameForConstraint())
|
||||
|
||||
case got.IsObjectType():
|
||||
for name, gotAty := range got.AttributeTypes() {
|
||||
if gotAty.Equals(wantEty) {
|
||||
continue // exact match, so no problem
|
||||
}
|
||||
if conv := getConversion(gotAty, wantEty, true); conv != nil {
|
||||
continue // conversion is available, so no problem
|
||||
}
|
||||
return fmt.Sprintf("element %q: %s", name, MismatchMessage(gotAty, wantEty))
|
||||
}
|
||||
|
||||
// If we get down here then something weird is going on but we'll
|
||||
// return a reasonable fallback message anyway.
|
||||
return fmt.Sprintf("all elements must be %s", wantEty.FriendlyNameForConstraint())
|
||||
|
||||
default:
|
||||
// Should not be possible to get here since we only call this function
|
||||
// with got as structural types, but...
|
||||
return want.FriendlyNameForConstraint() + " required"
|
||||
}
|
||||
}
|
||||
|
||||
func mismatchMessageCollectionsFromCollections(got, want cty.Type) string {
|
||||
// First some straightforward cases where the kind is just altogether wrong.
|
||||
switch {
|
||||
case want.IsListType() && !(got.IsListType() || got.IsSetType()):
|
||||
return want.FriendlyNameForConstraint() + " required"
|
||||
case want.IsSetType() && !(got.IsListType() || got.IsSetType()):
|
||||
return want.FriendlyNameForConstraint() + " required"
|
||||
case want.IsMapType() && !got.IsMapType():
|
||||
return want.FriendlyNameForConstraint() + " required"
|
||||
}
|
||||
|
||||
// If the kinds are matched well enough then we'll check the element types.
|
||||
gotEty := got.ElementType()
|
||||
wantEty := want.ElementType()
|
||||
noun := "element type"
|
||||
switch {
|
||||
case want.IsListType():
|
||||
noun = "list element type"
|
||||
case want.IsSetType():
|
||||
noun = "set element type"
|
||||
case want.IsMapType():
|
||||
noun = "map element type"
|
||||
}
|
||||
return fmt.Sprintf("incorrect %s: %s", noun, MismatchMessage(gotEty, wantEty))
|
||||
}
|
83
vendor/github.com/zclconf/go-cty/cty/convert/public.go
generated
vendored
Normal file
83
vendor/github.com/zclconf/go-cty/cty/convert/public.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// This file contains the public interface of this package, which is intended
|
||||
// to be a small, convenient interface designed for easy integration into
|
||||
// a hypothetical language type checker and interpreter.
|
||||
|
||||
// Conversion is a named function type representing a conversion from a
|
||||
// value of one type to a value of another type.
|
||||
//
|
||||
// The source type for a conversion is always the source type given to
|
||||
// the function that returned the Conversion, but there is no way to recover
|
||||
// that from a Conversion value itself. If a Conversion is given a value
|
||||
// that is not of its expected type (with the exception of DynamicPseudoType,
|
||||
// which is always supported) then the function may panic or produce undefined
|
||||
// results.
|
||||
type Conversion func(in cty.Value) (out cty.Value, err error)
|
||||
|
||||
// GetConversion returns a Conversion between the given in and out Types if
|
||||
// a safe one is available, or returns nil otherwise.
|
||||
func GetConversion(in cty.Type, out cty.Type) Conversion {
|
||||
return retConversion(getConversion(in, out, false))
|
||||
}
|
||||
|
||||
// GetConversionUnsafe returns a Conversion between the given in and out Types
|
||||
// if either a safe or unsafe one is available, or returns nil otherwise.
|
||||
func GetConversionUnsafe(in cty.Type, out cty.Type) Conversion {
|
||||
return retConversion(getConversion(in, out, true))
|
||||
}
|
||||
|
||||
// Convert returns the result of converting the given value to the given type
|
||||
// if an safe or unsafe conversion is available, or returns an error if such a
|
||||
// conversion is impossible.
|
||||
//
|
||||
// This is a convenience wrapper around calling GetConversionUnsafe and then
|
||||
// immediately passing the given value to the resulting function.
|
||||
func Convert(in cty.Value, want cty.Type) (cty.Value, error) {
|
||||
if in.Type().Equals(want) {
|
||||
return in, nil
|
||||
}
|
||||
|
||||
conv := GetConversionUnsafe(in.Type(), want)
|
||||
if conv == nil {
|
||||
return cty.NilVal, errors.New(MismatchMessage(in.Type(), want))
|
||||
}
|
||||
return conv(in)
|
||||
}
|
||||
|
||||
// Unify attempts to find the most general type that can be converted from
|
||||
// all of the given types. If this is possible, that type is returned along
|
||||
// with a slice of necessary conversions for some of the given types.
|
||||
//
|
||||
// If no common supertype can be found, this function returns cty.NilType and
|
||||
// a nil slice.
|
||||
//
|
||||
// If a common supertype *can* be found, the returned slice will always be
|
||||
// non-nil and will contain a non-nil conversion for each given type that
|
||||
// needs to be converted, with indices corresponding to the input slice.
|
||||
// Any given type that does *not* need conversion (because it is already of
|
||||
// the appropriate type) will have a nil Conversion.
|
||||
//
|
||||
// cty.DynamicPseudoType is, as usual, a special case. If the given type list
|
||||
// contains a mixture of dynamic and non-dynamic types, the dynamic types are
|
||||
// disregarded for type selection and a conversion is returned for them that
|
||||
// will attempt a late conversion of the given value to the target type,
|
||||
// failing with a conversion error if the eventual concrete type is not
|
||||
// compatible. If *all* given types are DynamicPseudoType, or in the
|
||||
// degenerate case of an empty slice of types, the returned type is itself
|
||||
// cty.DynamicPseudoType and no conversions are attempted.
|
||||
func Unify(types []cty.Type) (cty.Type, []Conversion) {
|
||||
return unify(types, false)
|
||||
}
|
||||
|
||||
// UnifyUnsafe is the same as Unify except that it may return unsafe
|
||||
// conversions in situations where a safe conversion isn't also available.
|
||||
func UnifyUnsafe(types []cty.Type) (cty.Type, []Conversion) {
|
||||
return unify(types, true)
|
||||
}
|
69
vendor/github.com/zclconf/go-cty/cty/convert/sort_types.go
generated
vendored
Normal file
69
vendor/github.com/zclconf/go-cty/cty/convert/sort_types.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// sortTypes produces an ordering of the given types that serves as a
|
||||
// preference order for the result of unification of the given types.
|
||||
// The return value is a slice of indices into the given slice, and will
|
||||
// thus always be the same length as the given slice.
|
||||
//
|
||||
// The goal is that the most general of the given types will appear first
|
||||
// in the ordering. If there are uncomparable pairs of types in the list
|
||||
// then they will appear in an undefined order, and the unification pass
|
||||
// will presumably then fail.
|
||||
func sortTypes(tys []cty.Type) []int {
|
||||
l := len(tys)
|
||||
|
||||
// First we build a graph whose edges represent "more general than",
|
||||
// which we will then do a topological sort of.
|
||||
edges := make([][]int, l)
|
||||
for i := 0; i < (l - 1); i++ {
|
||||
for j := i + 1; j < l; j++ {
|
||||
cmp := compareTypes(tys[i], tys[j])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
edges[i] = append(edges[i], j)
|
||||
case cmp > 0:
|
||||
edges[j] = append(edges[j], i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the in-degree of each node
|
||||
inDegree := make([]int, l)
|
||||
for _, outs := range edges {
|
||||
for _, j := range outs {
|
||||
inDegree[j]++
|
||||
}
|
||||
}
|
||||
|
||||
// The array backing our result will double as our queue for visiting
|
||||
// the nodes, with the queue slice moving along this array until it
|
||||
// is empty and positioned at the end of the array. Thus our visiting
|
||||
// order is also our result order.
|
||||
result := make([]int, l)
|
||||
queue := result[0:0]
|
||||
|
||||
// Initialize the queue with any item of in-degree 0, preserving
|
||||
// their relative order.
|
||||
for i, n := range inDegree {
|
||||
if n == 0 {
|
||||
queue = append(queue, i)
|
||||
}
|
||||
}
|
||||
|
||||
for len(queue) != 0 {
|
||||
i := queue[0]
|
||||
queue = queue[1:]
|
||||
for _, j := range edges[i] {
|
||||
inDegree[j]--
|
||||
if inDegree[j] == 0 {
|
||||
queue = append(queue, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
501
vendor/github.com/zclconf/go-cty/cty/convert/unify.go
generated
vendored
Normal file
501
vendor/github.com/zclconf/go-cty/cty/convert/unify.go
generated
vendored
Normal file
@ -0,0 +1,501 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// The current unify implementation is somewhat inefficient, but we accept this
|
||||
// under the assumption that it will generally be used with small numbers of
|
||||
// types and with types of reasonable complexity. However, it does have a
|
||||
// "happy path" where all of the given types are equal.
|
||||
//
|
||||
// This function is likely to have poor performance in cases where any given
|
||||
// types are very complex (lots of deeply-nested structures) or if the list
|
||||
// of types itself is very large. In particular, it will walk the nested type
|
||||
// structure under the given types several times, especially when given a
|
||||
// list of types for which unification is not possible, since each permutation
|
||||
// will be tried to determine that result.
|
||||
func unify(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
|
||||
if len(types) == 0 {
|
||||
// Degenerate case
|
||||
return cty.NilType, nil
|
||||
}
|
||||
|
||||
// If all of the given types are of the same structural kind, we may be
|
||||
// able to construct a new type that they can all be unified to, even if
|
||||
// that is not one of the given types. We must try this before the general
|
||||
// behavior below because in unsafe mode we can convert an object type to
|
||||
// a subset of that type, which would be a much less useful conversion for
|
||||
// unification purposes.
|
||||
{
|
||||
mapCt := 0
|
||||
listCt := 0
|
||||
setCt := 0
|
||||
objectCt := 0
|
||||
tupleCt := 0
|
||||
dynamicCt := 0
|
||||
for _, ty := range types {
|
||||
switch {
|
||||
case ty.IsMapType():
|
||||
mapCt++
|
||||
case ty.IsListType():
|
||||
listCt++
|
||||
case ty.IsSetType():
|
||||
setCt++
|
||||
case ty.IsObjectType():
|
||||
objectCt++
|
||||
case ty.IsTupleType():
|
||||
tupleCt++
|
||||
case ty == cty.DynamicPseudoType:
|
||||
dynamicCt++
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case mapCt > 0 && (mapCt+dynamicCt) == len(types):
|
||||
return unifyCollectionTypes(cty.Map, types, unsafe, dynamicCt > 0)
|
||||
|
||||
case mapCt > 0 && (mapCt+objectCt+dynamicCt) == len(types):
|
||||
// Objects often contain map data, but are not directly typed as
|
||||
// such due to language constructs or function types. Try to unify
|
||||
// them as maps first before falling back to heterogeneous type
|
||||
// conversion.
|
||||
ty, convs := unifyObjectsAsMaps(types, unsafe)
|
||||
// If we got a map back, we know the unification was successful.
|
||||
if ty.IsMapType() {
|
||||
return ty, convs
|
||||
}
|
||||
case listCt > 0 && (listCt+dynamicCt) == len(types):
|
||||
return unifyCollectionTypes(cty.List, types, unsafe, dynamicCt > 0)
|
||||
case listCt > 0 && (listCt+tupleCt+dynamicCt) == len(types):
|
||||
// Tuples are often lists in disguise, and we may be able to
|
||||
// unify them as such.
|
||||
ty, convs := unifyTuplesAsList(types, unsafe)
|
||||
// if we got a list back, we know the unification was successful.
|
||||
// Otherwise we will fall back to the heterogeneous type codepath.
|
||||
if ty.IsListType() {
|
||||
return ty, convs
|
||||
}
|
||||
case setCt > 0 && (setCt+dynamicCt) == len(types):
|
||||
return unifyCollectionTypes(cty.Set, 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
|
||||
}
|
||||
|
||||
// unifyTuplesAsList attempts to first see if the tuples unify as lists, then
|
||||
// re-unifies the given types with the list in place of the tuples.
|
||||
func unifyTuplesAsList(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
|
||||
var tuples []cty.Type
|
||||
var tupleIdxs []int
|
||||
for i, t := range types {
|
||||
if t.IsTupleType() {
|
||||
tuples = append(tuples, t)
|
||||
tupleIdxs = append(tupleIdxs, i)
|
||||
}
|
||||
}
|
||||
|
||||
ty, tupleConvs := unifyTupleTypesToList(tuples, unsafe)
|
||||
if !ty.IsListType() {
|
||||
return cty.NilType, nil
|
||||
}
|
||||
|
||||
// the tuples themselves unified as a list, get the overall
|
||||
// unification with this list type instead of the tuple.
|
||||
// make a copy of the types, so we can fallback to the standard
|
||||
// codepath if something went wrong
|
||||
listed := make([]cty.Type, len(types))
|
||||
copy(listed, types)
|
||||
for _, idx := range tupleIdxs {
|
||||
listed[idx] = ty
|
||||
}
|
||||
|
||||
newTy, convs := unify(listed, unsafe)
|
||||
if !newTy.IsListType() {
|
||||
return cty.NilType, nil
|
||||
}
|
||||
|
||||
// we have a good conversion, wrap the nested tuple conversions.
|
||||
// We know the tuple conversion is not nil, because we went from tuple to
|
||||
// list
|
||||
for i, idx := range tupleIdxs {
|
||||
listConv := convs[idx]
|
||||
tupleConv := tupleConvs[i]
|
||||
|
||||
if listConv == nil {
|
||||
convs[idx] = tupleConv
|
||||
continue
|
||||
}
|
||||
|
||||
convs[idx] = func(in cty.Value) (out cty.Value, err error) {
|
||||
out, err = tupleConv(in)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
return listConv(in)
|
||||
}
|
||||
}
|
||||
|
||||
return newTy, convs
|
||||
}
|
||||
|
||||
// unifyObjectsAsMaps attempts to first see if the objects unify as maps, then
|
||||
// re-unifies the given types with the map in place of the objects.
|
||||
func unifyObjectsAsMaps(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
|
||||
var objs []cty.Type
|
||||
var objIdxs []int
|
||||
for i, t := range types {
|
||||
if t.IsObjectType() {
|
||||
objs = append(objs, t)
|
||||
objIdxs = append(objIdxs, i)
|
||||
}
|
||||
}
|
||||
|
||||
ty, objConvs := unifyObjectTypesToMap(objs, unsafe)
|
||||
if !ty.IsMapType() {
|
||||
return cty.NilType, nil
|
||||
}
|
||||
|
||||
// the objects themselves unified as a map, get the overall
|
||||
// unification with this map type instead of the object.
|
||||
// Make a copy of the types, so we can fallback to the standard codepath if
|
||||
// something went wrong without changing the original types.
|
||||
mapped := make([]cty.Type, len(types))
|
||||
copy(mapped, types)
|
||||
for _, idx := range objIdxs {
|
||||
mapped[idx] = ty
|
||||
}
|
||||
|
||||
newTy, convs := unify(mapped, unsafe)
|
||||
if !newTy.IsMapType() {
|
||||
return cty.NilType, nil
|
||||
}
|
||||
|
||||
// we have a good conversion, so wrap the nested object conversions.
|
||||
// We know the object conversion is not nil, because we went from object to
|
||||
// map.
|
||||
for i, idx := range objIdxs {
|
||||
mapConv := convs[idx]
|
||||
objConv := objConvs[i]
|
||||
|
||||
if mapConv == nil {
|
||||
convs[idx] = objConv
|
||||
continue
|
||||
}
|
||||
|
||||
convs[idx] = func(in cty.Value) (out cty.Value, err error) {
|
||||
out, err = objConv(in)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
return mapConv(in)
|
||||
}
|
||||
}
|
||||
|
||||
return newTy, convs
|
||||
}
|
||||
|
||||
func unifyCollectionTypes(collectionType func(cty.Type) cty.Type, 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 := collectionType(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/zclconf/go-cty/cty/doc.go
generated
vendored
Normal file
18
vendor/github.com/zclconf/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/zclconf/go-cty/cty/element_iterator.go
generated
vendored
Normal file
194
vendor/github.com/zclconf/go-cty/cty/element_iterator.go
generated
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
package cty
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/zclconf/go-cty/cty/set"
|
||||
)
|
||||
|
||||
// ElementIterator is the interface type returned by Value.ElementIterator to
|
||||
// allow the caller to iterate over elements of a collection-typed value.
|
||||
//
|
||||
// Its usage pattern is as follows:
|
||||
//
|
||||
// it := val.ElementIterator()
|
||||
// for it.Next() {
|
||||
// key, val := it.Element()
|
||||
// // ...
|
||||
// }
|
||||
type ElementIterator interface {
|
||||
Next() bool
|
||||
Element() (key Value, value Value)
|
||||
}
|
||||
|
||||
func canElementIterator(val Value) bool {
|
||||
switch {
|
||||
case val.IsMarked():
|
||||
return false
|
||||
case val.ty.IsListType():
|
||||
return true
|
||||
case val.ty.IsMapType():
|
||||
return true
|
||||
case val.ty.IsSetType():
|
||||
return true
|
||||
case val.ty.IsTupleType():
|
||||
return true
|
||||
case val.ty.IsObjectType():
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func elementIterator(val Value) ElementIterator {
|
||||
val.assertUnmarked()
|
||||
switch {
|
||||
case val.ty.IsListType():
|
||||
return &listElementIterator{
|
||||
ety: val.ty.ElementType(),
|
||||
vals: val.v.([]interface{}),
|
||||
idx: -1,
|
||||
}
|
||||
case val.ty.IsMapType():
|
||||
// We iterate the keys in a predictable lexicographical order so
|
||||
// that results will always be stable given the same input map.
|
||||
rawMap := val.v.(map[string]interface{})
|
||||
keys := make([]string, 0, len(rawMap))
|
||||
for key := range rawMap {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
return &mapElementIterator{
|
||||
ety: val.ty.ElementType(),
|
||||
vals: rawMap,
|
||||
keys: keys,
|
||||
idx: -1,
|
||||
}
|
||||
case val.ty.IsSetType():
|
||||
rawSet := val.v.(set.Set)
|
||||
return &setElementIterator{
|
||||
ety: val.ty.ElementType(),
|
||||
setIt: rawSet.Iterator(),
|
||||
}
|
||||
case val.ty.IsTupleType():
|
||||
return &tupleElementIterator{
|
||||
etys: val.ty.TupleElementTypes(),
|
||||
vals: val.v.([]interface{}),
|
||||
idx: -1,
|
||||
}
|
||||
case val.ty.IsObjectType():
|
||||
// We iterate the keys in a predictable lexicographical order so
|
||||
// that results will always be stable given the same object type.
|
||||
atys := val.ty.AttributeTypes()
|
||||
keys := make([]string, 0, len(atys))
|
||||
for key := range atys {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
return &objectElementIterator{
|
||||
atys: atys,
|
||||
vals: val.v.(map[string]interface{}),
|
||||
attrNames: keys,
|
||||
idx: -1,
|
||||
}
|
||||
default:
|
||||
panic("attempt to iterate on non-collection, non-tuple type")
|
||||
}
|
||||
}
|
||||
|
||||
type listElementIterator struct {
|
||||
ety Type
|
||||
vals []interface{}
|
||||
idx int
|
||||
}
|
||||
|
||||
func (it *listElementIterator) Element() (Value, Value) {
|
||||
i := it.idx
|
||||
return NumberIntVal(int64(i)), Value{
|
||||
ty: it.ety,
|
||||
v: it.vals[i],
|
||||
}
|
||||
}
|
||||
|
||||
func (it *listElementIterator) Next() bool {
|
||||
it.idx++
|
||||
return it.idx < len(it.vals)
|
||||
}
|
||||
|
||||
type mapElementIterator struct {
|
||||
ety Type
|
||||
vals map[string]interface{}
|
||||
keys []string
|
||||
idx int
|
||||
}
|
||||
|
||||
func (it *mapElementIterator) Element() (Value, Value) {
|
||||
key := it.keys[it.idx]
|
||||
return StringVal(key), Value{
|
||||
ty: it.ety,
|
||||
v: it.vals[key],
|
||||
}
|
||||
}
|
||||
|
||||
func (it *mapElementIterator) Next() bool {
|
||||
it.idx++
|
||||
return it.idx < len(it.keys)
|
||||
}
|
||||
|
||||
type setElementIterator struct {
|
||||
ety Type
|
||||
setIt *set.Iterator
|
||||
}
|
||||
|
||||
func (it *setElementIterator) Element() (Value, Value) {
|
||||
val := Value{
|
||||
ty: it.ety,
|
||||
v: it.setIt.Value(),
|
||||
}
|
||||
return val, val
|
||||
}
|
||||
|
||||
func (it *setElementIterator) Next() bool {
|
||||
return it.setIt.Next()
|
||||
}
|
||||
|
||||
type tupleElementIterator struct {
|
||||
etys []Type
|
||||
vals []interface{}
|
||||
idx int
|
||||
}
|
||||
|
||||
func (it *tupleElementIterator) Element() (Value, Value) {
|
||||
i := it.idx
|
||||
return NumberIntVal(int64(i)), Value{
|
||||
ty: it.etys[i],
|
||||
v: it.vals[i],
|
||||
}
|
||||
}
|
||||
|
||||
func (it *tupleElementIterator) Next() bool {
|
||||
it.idx++
|
||||
return it.idx < len(it.vals)
|
||||
}
|
||||
|
||||
type objectElementIterator struct {
|
||||
atys map[string]Type
|
||||
vals map[string]interface{}
|
||||
attrNames []string
|
||||
idx int
|
||||
}
|
||||
|
||||
func (it *objectElementIterator) Element() (Value, Value) {
|
||||
key := it.attrNames[it.idx]
|
||||
return StringVal(key), Value{
|
||||
ty: it.atys[key],
|
||||
v: it.vals[key],
|
||||
}
|
||||
}
|
||||
|
||||
func (it *objectElementIterator) Next() bool {
|
||||
it.idx++
|
||||
return it.idx < len(it.attrNames)
|
||||
}
|
55
vendor/github.com/zclconf/go-cty/cty/error.go
generated
vendored
Normal file
55
vendor/github.com/zclconf/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,
|
||||
}
|
||||
}
|
70
vendor/github.com/zclconf/go-cty/cty/function/argument.go
generated
vendored
Normal file
70
vendor/github.com/zclconf/go-cty/cty/function/argument.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Parameter represents a parameter to a function.
|
||||
type Parameter struct {
|
||||
// Name is an optional name for the argument. This package ignores this
|
||||
// value, but callers may use it for documentation, etc.
|
||||
Name string
|
||||
|
||||
// A type that any argument for this parameter must conform to.
|
||||
// cty.DynamicPseudoType can be used, either at top-level or nested
|
||||
// in a parameterized type, to indicate that any type should be
|
||||
// permitted, to allow the definition of type-generic functions.
|
||||
Type cty.Type
|
||||
|
||||
// If AllowNull is set then null values may be passed into this
|
||||
// argument's slot in both the type-check function and the implementation
|
||||
// function. If not set, such values are rejected by the built-in
|
||||
// checking rules.
|
||||
AllowNull bool
|
||||
|
||||
// If AllowUnknown is set then unknown values may be passed into this
|
||||
// argument's slot in the implementation function. If not set, any
|
||||
// unknown values will cause the function to immediately return
|
||||
// an unkonwn value without calling the implementation function, thus
|
||||
// freeing the function implementer from dealing with this case.
|
||||
AllowUnknown bool
|
||||
|
||||
// If AllowDynamicType is set then DynamicVal may be passed into this
|
||||
// argument's slot in the implementation function. If not set, any
|
||||
// dynamic values will cause the function to immediately return
|
||||
// DynamicVal value without calling the implementation function, thus
|
||||
// freeing the function implementer from dealing with this case.
|
||||
//
|
||||
// Note that DynamicVal is also unknown, so in order to receive dynamic
|
||||
// *values* it is also necessary to set AllowUnknown.
|
||||
//
|
||||
// However, it is valid to set AllowDynamicType without AllowUnknown, in
|
||||
// which case a dynamic value may be passed to the type checking function
|
||||
// but will not make it to the *implementation* function. Instead, an
|
||||
// unknown value of the type returned by the type-check function will be
|
||||
// returned. This is suggested for functions that have a static return
|
||||
// type since it allows the return value to be typed even if the input
|
||||
// values are not, thus improving the type-check accuracy of derived
|
||||
// values.
|
||||
AllowDynamicType bool
|
||||
|
||||
// If AllowMarked is set then marked values may be passed into this
|
||||
// argument's slot in the implementation function. If not set, any
|
||||
// marked value will be unmarked before calling and then the markings
|
||||
// from that value will be applied automatically to the function result,
|
||||
// ensuring that the marks get propagated in a simplistic way even if
|
||||
// a function is unable to handle them.
|
||||
//
|
||||
// For any argument whose parameter has AllowMarked set, it's the
|
||||
// function implementation's responsibility to Unmark the given value
|
||||
// and propagate the marks appropriatedly to the result in order to
|
||||
// avoid losing the marks. Application-specific functions might use
|
||||
// special rules to selectively propagate particular marks.
|
||||
//
|
||||
// The automatic unmarking of values applies only to the main
|
||||
// implementation function. In an application that uses marked values,
|
||||
// the Type implementation for a function must always be prepared to accept
|
||||
// marked values, which is easy to achieve by consulting only the type
|
||||
// and ignoring the value itself.
|
||||
AllowMarked bool
|
||||
}
|
6
vendor/github.com/zclconf/go-cty/cty/function/doc.go
generated
vendored
Normal file
6
vendor/github.com/zclconf/go-cty/cty/function/doc.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
// Package function builds on the functionality of cty by modeling functions
|
||||
// that operate on cty Values.
|
||||
//
|
||||
// Functions are, at their core, Go anonymous functions. However, this package
|
||||
// wraps around them utility functions for parameter type checking, etc.
|
||||
package function
|
50
vendor/github.com/zclconf/go-cty/cty/function/error.go
generated
vendored
Normal file
50
vendor/github.com/zclconf/go-cty/cty/function/error.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
// ArgError represents an error with one of the arguments in a call. The
|
||||
// attribute Index represents the zero-based index of the argument in question.
|
||||
//
|
||||
// Its error *may* be a cty.PathError, in which case the error actually
|
||||
// pertains to a nested value within the data structure passed as the argument.
|
||||
type ArgError struct {
|
||||
error
|
||||
Index int
|
||||
}
|
||||
|
||||
func NewArgErrorf(i int, f string, args ...interface{}) error {
|
||||
return ArgError{
|
||||
error: fmt.Errorf(f, args...),
|
||||
Index: i,
|
||||
}
|
||||
}
|
||||
|
||||
func NewArgError(i int, err error) error {
|
||||
return ArgError{
|
||||
error: err,
|
||||
Index: i,
|
||||
}
|
||||
}
|
||||
|
||||
// PanicError indicates that a panic occurred while executing either a
|
||||
// function's type or implementation function. This is captured and wrapped
|
||||
// into a normal error so that callers (expected to be language runtimes)
|
||||
// are freed from having to deal with panics in buggy functions.
|
||||
type PanicError struct {
|
||||
Value interface{}
|
||||
Stack []byte
|
||||
}
|
||||
|
||||
func errorForPanic(val interface{}) error {
|
||||
return PanicError{
|
||||
Value: val,
|
||||
Stack: debug.Stack(),
|
||||
}
|
||||
}
|
||||
|
||||
func (e PanicError) Error() string {
|
||||
return fmt.Sprintf("panic in function implementation: %s\n%s", e.Value, e.Stack)
|
||||
}
|
346
vendor/github.com/zclconf/go-cty/cty/function/function.go
generated
vendored
Normal file
346
vendor/github.com/zclconf/go-cty/cty/function/function.go
generated
vendored
Normal file
@ -0,0 +1,346 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Function represents a function. This is the main type in this package.
|
||||
type Function struct {
|
||||
spec *Spec
|
||||
}
|
||||
|
||||
// Spec is the specification of a function, used to instantiate
|
||||
// a new Function.
|
||||
type Spec struct {
|
||||
// Params is a description of the positional parameters for the function.
|
||||
// The standard checking logic rejects any calls that do not provide
|
||||
// arguments conforming to this definition, freeing the function
|
||||
// implementer from dealing with such inconsistencies.
|
||||
Params []Parameter
|
||||
|
||||
// VarParam is an optional specification of additional "varargs" the
|
||||
// function accepts. If this is non-nil then callers may provide an
|
||||
// arbitrary number of additional arguments (after those matching with
|
||||
// the fixed parameters in Params) that conform to the given specification,
|
||||
// which will appear as additional values in the slices of values
|
||||
// provided to the type and implementation functions.
|
||||
VarParam *Parameter
|
||||
|
||||
// Type is the TypeFunc that decides the return type of the function
|
||||
// given its arguments, which may be Unknown. See the documentation
|
||||
// of TypeFunc for more information.
|
||||
//
|
||||
// Use StaticReturnType if the function's return type does not vary
|
||||
// depending on its arguments.
|
||||
Type TypeFunc
|
||||
|
||||
// Impl is the ImplFunc that implements the function's behavior.
|
||||
//
|
||||
// Functions are expected to behave as pure functions, and not create
|
||||
// any visible side-effects.
|
||||
//
|
||||
// If a TypeFunc is also provided, the value returned from Impl *must*
|
||||
// conform to the type it returns, or a call to the function will panic.
|
||||
Impl ImplFunc
|
||||
}
|
||||
|
||||
// New creates a new function with the given specification.
|
||||
//
|
||||
// After passing a Spec to this function, the caller must no longer read from
|
||||
// or mutate it.
|
||||
func New(spec *Spec) Function {
|
||||
f := Function{
|
||||
spec: spec,
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// TypeFunc is a callback type for determining the return type of a function
|
||||
// given its arguments.
|
||||
//
|
||||
// Any of the values passed to this function may be unknown, even if the
|
||||
// parameters are not configured to accept unknowns.
|
||||
//
|
||||
// If any of the given values are *not* unknown, the TypeFunc may use the
|
||||
// values for pre-validation and for choosing the return type. For example,
|
||||
// a hypothetical JSON-unmarshalling function could return
|
||||
// cty.DynamicPseudoType if the given JSON string is unknown, but return
|
||||
// a concrete type based on the JSON structure if the JSON string is already
|
||||
// known.
|
||||
type TypeFunc func(args []cty.Value) (cty.Type, error)
|
||||
|
||||
// ImplFunc is a callback type for the main implementation of a function.
|
||||
//
|
||||
// "args" are the values for the arguments, and this slice will always be at
|
||||
// least as long as the argument definition slice for the function.
|
||||
//
|
||||
// "retType" is the type returned from the Type callback, included as a
|
||||
// convenience to avoid the need to re-compute the return type for generic
|
||||
// functions whose return type is a function of the arguments.
|
||||
type ImplFunc func(args []cty.Value, retType cty.Type) (cty.Value, error)
|
||||
|
||||
// StaticReturnType returns a TypeFunc that always returns the given type.
|
||||
//
|
||||
// This is provided as a convenience for defining a function whose return
|
||||
// type does not depend on the argument types.
|
||||
func StaticReturnType(ty cty.Type) TypeFunc {
|
||||
return func([]cty.Value) (cty.Type, error) {
|
||||
return ty, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ReturnType returns the return type of a function given a set of candidate
|
||||
// argument types, or returns an error if the given types are unacceptable.
|
||||
//
|
||||
// If the caller already knows values for at least some of the arguments
|
||||
// it can be better to call ReturnTypeForValues, since certain functions may
|
||||
// determine their return types from their values and return DynamicVal if
|
||||
// the values are unknown.
|
||||
func (f Function) ReturnType(argTypes []cty.Type) (cty.Type, error) {
|
||||
vals := make([]cty.Value, len(argTypes))
|
||||
for i, ty := range argTypes {
|
||||
vals[i] = cty.UnknownVal(ty)
|
||||
}
|
||||
return f.ReturnTypeForValues(vals)
|
||||
}
|
||||
|
||||
// ReturnTypeForValues is similar to ReturnType but can be used if the caller
|
||||
// already knows the values of some or all of the arguments, in which case
|
||||
// the function may be able to determine a more definite result if its
|
||||
// return type depends on the argument *values*.
|
||||
//
|
||||
// For any arguments whose values are not known, pass an Unknown value of
|
||||
// the appropriate type.
|
||||
func (f Function) ReturnTypeForValues(args []cty.Value) (ty cty.Type, err error) {
|
||||
var posArgs []cty.Value
|
||||
var varArgs []cty.Value
|
||||
|
||||
if f.spec.VarParam == nil {
|
||||
if len(args) != len(f.spec.Params) {
|
||||
return cty.Type{}, fmt.Errorf(
|
||||
"wrong number of arguments (%d required; %d given)",
|
||||
len(f.spec.Params), len(args),
|
||||
)
|
||||
}
|
||||
|
||||
posArgs = args
|
||||
varArgs = nil
|
||||
} else {
|
||||
if len(args) < len(f.spec.Params) {
|
||||
return cty.Type{}, fmt.Errorf(
|
||||
"wrong number of arguments (at least %d required; %d given)",
|
||||
len(f.spec.Params), len(args),
|
||||
)
|
||||
}
|
||||
|
||||
posArgs = args[0:len(f.spec.Params)]
|
||||
varArgs = args[len(f.spec.Params):]
|
||||
}
|
||||
|
||||
for i, spec := range f.spec.Params {
|
||||
val := posArgs[i]
|
||||
|
||||
if val.ContainsMarked() && !spec.AllowMarked {
|
||||
// During type checking we just unmark values and discard their
|
||||
// marks, under the assumption that during actual execution of
|
||||
// the function we'll do similarly and then re-apply the marks
|
||||
// afterwards. Note that this does mean that a function that
|
||||
// inspects values (rather than just types) in its Type
|
||||
// implementation can potentially fail to take into account marks,
|
||||
// unless it specifically opts in to seeing them.
|
||||
unmarked, _ := val.UnmarkDeep()
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[i] = unmarked
|
||||
args = newArgs
|
||||
}
|
||||
|
||||
if val.IsNull() && !spec.AllowNull {
|
||||
return cty.Type{}, NewArgErrorf(i, "argument must not be null")
|
||||
}
|
||||
|
||||
// AllowUnknown is ignored for type-checking, since we expect to be
|
||||
// able to type check with unknown values. We *do* still need to deal
|
||||
// with DynamicPseudoType here though, since the Type function might
|
||||
// not be ready to deal with that.
|
||||
|
||||
if val.Type() == cty.DynamicPseudoType {
|
||||
if !spec.AllowDynamicType {
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
} else if errs := val.Type().TestConformance(spec.Type); errs != nil {
|
||||
// For now we'll just return the first error in the set, since
|
||||
// we don't have a good way to return the whole list here.
|
||||
// Would be good to do something better at some point...
|
||||
return cty.Type{}, NewArgError(i, errs[0])
|
||||
}
|
||||
}
|
||||
|
||||
if varArgs != nil {
|
||||
spec := f.spec.VarParam
|
||||
for i, val := range varArgs {
|
||||
realI := i + len(posArgs)
|
||||
|
||||
if val.ContainsMarked() && !spec.AllowMarked {
|
||||
// See the similar block in the loop above for what's going on here.
|
||||
unmarked, _ := val.UnmarkDeep()
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[realI] = unmarked
|
||||
args = newArgs
|
||||
}
|
||||
|
||||
if val.IsNull() && !spec.AllowNull {
|
||||
return cty.Type{}, NewArgErrorf(realI, "argument must not be null")
|
||||
}
|
||||
|
||||
if val.Type() == cty.DynamicPseudoType {
|
||||
if !spec.AllowDynamicType {
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
} else if errs := val.Type().TestConformance(spec.Type); errs != nil {
|
||||
// For now we'll just return the first error in the set, since
|
||||
// we don't have a good way to return the whole list here.
|
||||
// Would be good to do something better at some point...
|
||||
return cty.Type{}, NewArgError(i, errs[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Intercept any panics from the function and return them as normal errors,
|
||||
// so a calling language runtime doesn't need to deal with panics.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ty = cty.NilType
|
||||
err = errorForPanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
return f.spec.Type(args)
|
||||
}
|
||||
|
||||
// Call actually calls the function with the given arguments, which must
|
||||
// conform to the function's parameter specification or an error will be
|
||||
// returned.
|
||||
func (f Function) Call(args []cty.Value) (val cty.Value, err error) {
|
||||
expectedType, err := f.ReturnTypeForValues(args)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
|
||||
// Type checking already dealt with most situations relating to our
|
||||
// parameter specification, but we still need to deal with unknown
|
||||
// values and marked values.
|
||||
posArgs := args[:len(f.spec.Params)]
|
||||
varArgs := args[len(f.spec.Params):]
|
||||
var resultMarks []cty.ValueMarks
|
||||
|
||||
for i, spec := range f.spec.Params {
|
||||
val := posArgs[i]
|
||||
|
||||
if !val.IsKnown() && !spec.AllowUnknown {
|
||||
return cty.UnknownVal(expectedType), nil
|
||||
}
|
||||
|
||||
if !spec.AllowMarked {
|
||||
unwrappedVal, marks := val.UnmarkDeep()
|
||||
if len(marks) > 0 {
|
||||
// In order to avoid additional overhead on applications that
|
||||
// are not using marked values, we copy the given args only
|
||||
// if we encounter a marked value we need to unmark. However,
|
||||
// as a consequence we end up doing redundant copying if multiple
|
||||
// marked values need to be unwrapped. That seems okay because
|
||||
// argument lists are generally small.
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[i] = unwrappedVal
|
||||
resultMarks = append(resultMarks, marks)
|
||||
args = newArgs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if f.spec.VarParam != nil {
|
||||
spec := f.spec.VarParam
|
||||
for i, val := range varArgs {
|
||||
if !val.IsKnown() && !spec.AllowUnknown {
|
||||
return cty.UnknownVal(expectedType), nil
|
||||
}
|
||||
if !spec.AllowMarked {
|
||||
unwrappedVal, marks := val.UnmarkDeep()
|
||||
if len(marks) > 0 {
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[len(posArgs)+i] = unwrappedVal
|
||||
resultMarks = append(resultMarks, marks)
|
||||
args = newArgs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var retVal cty.Value
|
||||
{
|
||||
// Intercept any panics from the function and return them as normal errors,
|
||||
// so a calling language runtime doesn't need to deal with panics.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
val = cty.NilVal
|
||||
err = errorForPanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
retVal, err = f.spec.Impl(args, expectedType)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
if len(resultMarks) > 0 {
|
||||
retVal = retVal.WithMarks(resultMarks...)
|
||||
}
|
||||
}
|
||||
|
||||
// Returned value must conform to what the Type function expected, to
|
||||
// protect callers from having to deal with inconsistencies.
|
||||
if errs := retVal.Type().TestConformance(expectedType); errs != nil {
|
||||
panic(fmt.Errorf(
|
||||
"returned value %#v does not conform to expected return type %#v: %s",
|
||||
retVal, expectedType, errs[0],
|
||||
))
|
||||
}
|
||||
|
||||
return retVal, nil
|
||||
}
|
||||
|
||||
// ProxyFunc the type returned by the method Function.Proxy.
|
||||
type ProxyFunc func(args ...cty.Value) (cty.Value, error)
|
||||
|
||||
// Proxy returns a function that can be called with cty.Value arguments
|
||||
// to run the function. This is provided as a convenience for when using
|
||||
// a function directly within Go code.
|
||||
func (f Function) Proxy() ProxyFunc {
|
||||
return func(args ...cty.Value) (cty.Value, error) {
|
||||
return f.Call(args)
|
||||
}
|
||||
}
|
||||
|
||||
// Params returns information about the function's fixed positional parameters.
|
||||
// This does not include information about any variadic arguments accepted;
|
||||
// for that, call VarParam.
|
||||
func (f Function) Params() []Parameter {
|
||||
new := make([]Parameter, len(f.spec.Params))
|
||||
copy(new, f.spec.Params)
|
||||
return new
|
||||
}
|
||||
|
||||
// VarParam returns information about the variadic arguments the function
|
||||
// expects, or nil if the function is not variadic.
|
||||
func (f Function) VarParam() *Parameter {
|
||||
if f.spec.VarParam == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := *f.spec.VarParam
|
||||
return &ret
|
||||
}
|
78
vendor/github.com/zclconf/go-cty/cty/function/stdlib/bool.go
generated
vendored
Normal file
78
vendor/github.com/zclconf/go-cty/cty/function/stdlib/bool.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var NotFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "val",
|
||||
Type: cty.Bool,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return args[0].Not(), nil
|
||||
},
|
||||
})
|
||||
|
||||
var AndFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Bool,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Bool,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return args[0].And(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var OrFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Bool,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Bool,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return args[0].Or(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Not returns the logical complement of the given boolean value.
|
||||
func Not(num cty.Value) (cty.Value, error) {
|
||||
return NotFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// And returns true if and only if both of the given boolean values are true.
|
||||
func And(a, b cty.Value) (cty.Value, error) {
|
||||
return AndFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Or returns true if either of the given boolean values are true.
|
||||
func Or(a, b cty.Value) (cty.Value, error) {
|
||||
return OrFunc.Call([]cty.Value{a, b})
|
||||
}
|
112
vendor/github.com/zclconf/go-cty/cty/function/stdlib/bytes.go
generated
vendored
Normal file
112
vendor/github.com/zclconf/go-cty/cty/function/stdlib/bytes.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
// Bytes is a capsule type that can be used with the binary functions to
|
||||
// support applications that need to support raw buffers in addition to
|
||||
// UTF-8 strings.
|
||||
var Bytes = cty.Capsule("bytes", reflect.TypeOf([]byte(nil)))
|
||||
|
||||
// BytesVal creates a new Bytes value from the given buffer, which must be
|
||||
// non-nil or this function will panic.
|
||||
//
|
||||
// Once a byte slice has been wrapped in a Bytes capsule, its underlying array
|
||||
// must be considered immutable.
|
||||
func BytesVal(buf []byte) cty.Value {
|
||||
if buf == nil {
|
||||
panic("can't make Bytes value from nil slice")
|
||||
}
|
||||
|
||||
return cty.CapsuleVal(Bytes, &buf)
|
||||
}
|
||||
|
||||
// BytesLen is a Function that returns the length of the buffer encapsulated
|
||||
// in a Bytes value.
|
||||
var BytesLenFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "buf",
|
||||
Type: Bytes,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
bufPtr := args[0].EncapsulatedValue().(*[]byte)
|
||||
return cty.NumberIntVal(int64(len(*bufPtr))), nil
|
||||
},
|
||||
})
|
||||
|
||||
// BytesSlice is a Function that returns a slice of the given Bytes value.
|
||||
var BytesSliceFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "buf",
|
||||
Type: Bytes,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "offset",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "length",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(Bytes),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
bufPtr := args[0].EncapsulatedValue().(*[]byte)
|
||||
|
||||
var offset, length int
|
||||
|
||||
var err error
|
||||
err = gocty.FromCtyValue(args[1], &offset)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
err = gocty.FromCtyValue(args[2], &length)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
|
||||
if offset < 0 || length < 0 {
|
||||
return cty.NilVal, fmt.Errorf("offset and length must be non-negative")
|
||||
}
|
||||
|
||||
if offset > len(*bufPtr) {
|
||||
return cty.NilVal, fmt.Errorf(
|
||||
"offset %d is greater than total buffer length %d",
|
||||
offset, len(*bufPtr),
|
||||
)
|
||||
}
|
||||
|
||||
end := offset + length
|
||||
|
||||
if end > len(*bufPtr) {
|
||||
return cty.NilVal, fmt.Errorf(
|
||||
"offset %d + length %d is greater than total buffer length %d",
|
||||
offset, length, len(*bufPtr),
|
||||
)
|
||||
}
|
||||
|
||||
return BytesVal((*bufPtr)[offset:end]), nil
|
||||
},
|
||||
})
|
||||
|
||||
func BytesLen(buf cty.Value) (cty.Value, error) {
|
||||
return BytesLenFunc.Call([]cty.Value{buf})
|
||||
}
|
||||
|
||||
func BytesSlice(buf cty.Value, offset cty.Value, length cty.Value) (cty.Value, error) {
|
||||
return BytesSliceFunc.Call([]cty.Value{buf, offset, length})
|
||||
}
|
1453
vendor/github.com/zclconf/go-cty/cty/function/stdlib/collection.go
generated
vendored
Normal file
1453
vendor/github.com/zclconf/go-cty/cty/function/stdlib/collection.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
87
vendor/github.com/zclconf/go-cty/cty/function/stdlib/conversion.go
generated
vendored
Normal file
87
vendor/github.com/zclconf/go-cty/cty/function/stdlib/conversion.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// MakeToFunc constructs a "to..." function, like "tostring", which converts
|
||||
// its argument to a specific type or type kind.
|
||||
//
|
||||
// The given type wantTy can be any type constraint that cty's "convert" package
|
||||
// would accept. In particular, this means that you can pass
|
||||
// cty.List(cty.DynamicPseudoType) to mean "list of any single type", which
|
||||
// will then cause cty to attempt to unify all of the element types when given
|
||||
// a tuple.
|
||||
func MakeToFunc(wantTy cty.Type) function.Function {
|
||||
return function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "v",
|
||||
// We use DynamicPseudoType rather than wantTy here so that
|
||||
// all values will pass through the function API verbatim and
|
||||
// we can handle the conversion logic within the Type and
|
||||
// Impl functions. This allows us to customize the error
|
||||
// messages to be more appropriate for an explicit type
|
||||
// conversion, whereas the cty function system produces
|
||||
// messages aimed at _implicit_ type conversions.
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowNull: true,
|
||||
},
|
||||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
gotTy := args[0].Type()
|
||||
if gotTy.Equals(wantTy) {
|
||||
return wantTy, nil
|
||||
}
|
||||
conv := convert.GetConversionUnsafe(args[0].Type(), wantTy)
|
||||
if conv == nil {
|
||||
// We'll use some specialized errors for some trickier cases,
|
||||
// but most we can handle in a simple way.
|
||||
switch {
|
||||
case gotTy.IsTupleType() && wantTy.IsTupleType():
|
||||
return cty.NilType, function.NewArgErrorf(0, "incompatible tuple type for conversion: %s", convert.MismatchMessage(gotTy, wantTy))
|
||||
case gotTy.IsObjectType() && wantTy.IsObjectType():
|
||||
return cty.NilType, function.NewArgErrorf(0, "incompatible object type for conversion: %s", convert.MismatchMessage(gotTy, wantTy))
|
||||
default:
|
||||
return cty.NilType, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint())
|
||||
}
|
||||
}
|
||||
// If a conversion is available then everything is fine.
|
||||
return wantTy, nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
// We didn't set "AllowUnknown" on our argument, so it is guaranteed
|
||||
// to be known here but may still be null.
|
||||
ret, err := convert.Convert(args[0], retType)
|
||||
if err != nil {
|
||||
// Because we used GetConversionUnsafe above, conversion can
|
||||
// still potentially fail in here. For example, if the user
|
||||
// asks to convert the string "a" to bool then we'll
|
||||
// optimistically permit it during type checking but fail here
|
||||
// once we note that the value isn't either "true" or "false".
|
||||
gotTy := args[0].Type()
|
||||
switch {
|
||||
case gotTy == cty.String && wantTy == cty.Bool:
|
||||
what := "string"
|
||||
if !args[0].IsNull() {
|
||||
what = strconv.Quote(args[0].AsString())
|
||||
}
|
||||
return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to bool; only the strings "true" or "false" are allowed`, what)
|
||||
case gotTy == cty.String && wantTy == cty.Number:
|
||||
what := "string"
|
||||
if !args[0].IsNull() {
|
||||
what = strconv.Quote(args[0].AsString())
|
||||
}
|
||||
return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to number; given string must be a decimal representation of a number`, what)
|
||||
default:
|
||||
return cty.NilVal, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint())
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
},
|
||||
})
|
||||
}
|
102
vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go
generated
vendored
Normal file
102
vendor/github.com/zclconf/go-cty/cty/function/stdlib/csv.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var CSVDecodeFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
str := args[0]
|
||||
if !str.IsKnown() {
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
|
||||
r := strings.NewReader(str.AsString())
|
||||
cr := csv.NewReader(r)
|
||||
headers, err := cr.Read()
|
||||
if err == io.EOF {
|
||||
return cty.DynamicPseudoType, fmt.Errorf("missing header line")
|
||||
}
|
||||
if err != nil {
|
||||
return cty.DynamicPseudoType, csvError(err)
|
||||
}
|
||||
|
||||
atys := make(map[string]cty.Type, len(headers))
|
||||
for _, name := range headers {
|
||||
if _, exists := atys[name]; exists {
|
||||
return cty.DynamicPseudoType, fmt.Errorf("duplicate column name %q", name)
|
||||
}
|
||||
atys[name] = cty.String
|
||||
}
|
||||
return cty.List(cty.Object(atys)), nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
ety := retType.ElementType()
|
||||
atys := ety.AttributeTypes()
|
||||
str := args[0]
|
||||
r := strings.NewReader(str.AsString())
|
||||
cr := csv.NewReader(r)
|
||||
cr.FieldsPerRecord = len(atys)
|
||||
|
||||
// Read the header row first, since that'll tell us which indices
|
||||
// map to which attribute names.
|
||||
headers, err := cr.Read()
|
||||
if err != nil {
|
||||
return cty.DynamicVal, err
|
||||
}
|
||||
|
||||
var rows []cty.Value
|
||||
for {
|
||||
cols, err := cr.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return cty.DynamicVal, csvError(err)
|
||||
}
|
||||
|
||||
vals := make(map[string]cty.Value, len(cols))
|
||||
for i, str := range cols {
|
||||
name := headers[i]
|
||||
vals[name] = cty.StringVal(str)
|
||||
}
|
||||
rows = append(rows, cty.ObjectVal(vals))
|
||||
}
|
||||
|
||||
if len(rows) == 0 {
|
||||
return cty.ListValEmpty(ety), nil
|
||||
}
|
||||
return cty.ListVal(rows), nil
|
||||
},
|
||||
})
|
||||
|
||||
// CSVDecode parses the given CSV (RFC 4180) string and, if it is valid,
|
||||
// returns a list of objects representing the rows.
|
||||
//
|
||||
// The result is always a list of some object type. The first row of the
|
||||
// input is used to determine the object attributes, and subsequent rows
|
||||
// determine the values of those attributes.
|
||||
func CSVDecode(str cty.Value) (cty.Value, error) {
|
||||
return CSVDecodeFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
func csvError(err error) error {
|
||||
switch err := err.(type) {
|
||||
case *csv.ParseError:
|
||||
return fmt.Errorf("CSV parse error on line %d: %w", err.Line, err.Err)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
434
vendor/github.com/zclconf/go-cty/cty/function/stdlib/datetime.go
generated
vendored
Normal file
434
vendor/github.com/zclconf/go-cty/cty/function/stdlib/datetime.go
generated
vendored
Normal file
@ -0,0 +1,434 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var FormatDateFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "format",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "time",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
formatStr := args[0].AsString()
|
||||
timeStr := args[1].AsString()
|
||||
t, err := parseTimestamp(timeStr)
|
||||
if err != nil {
|
||||
return cty.DynamicVal, function.NewArgError(1, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
sc := bufio.NewScanner(strings.NewReader(formatStr))
|
||||
sc.Split(splitDateFormat)
|
||||
const esc = '\''
|
||||
for sc.Scan() {
|
||||
tok := sc.Bytes()
|
||||
|
||||
// The leading byte signals the token type
|
||||
switch {
|
||||
case tok[0] == esc:
|
||||
if tok[len(tok)-1] != esc || len(tok) == 1 {
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "unterminated literal '")
|
||||
}
|
||||
if len(tok) == 2 {
|
||||
// Must be a single escaped quote, ''
|
||||
buf.WriteByte(esc)
|
||||
} else {
|
||||
// The content (until a closing esc) is printed out verbatim
|
||||
// except that we must un-double any double-esc escapes in
|
||||
// the middle of the string.
|
||||
raw := tok[1 : len(tok)-1]
|
||||
for i := 0; i < len(raw); i++ {
|
||||
buf.WriteByte(raw[i])
|
||||
if raw[i] == esc {
|
||||
i++ // skip the escaped quote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case startsDateFormatVerb(tok[0]):
|
||||
switch tok[0] {
|
||||
case 'Y':
|
||||
y := t.Year()
|
||||
switch len(tok) {
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", y%100)
|
||||
case 4:
|
||||
fmt.Fprintf(&buf, "%04d", y)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: year must either be \"YY\" or \"YYYY\"", tok)
|
||||
}
|
||||
case 'M':
|
||||
m := t.Month()
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", m)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", m)
|
||||
case 3:
|
||||
buf.WriteString(m.String()[:3])
|
||||
case 4:
|
||||
buf.WriteString(m.String())
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: month must be \"M\", \"MM\", \"MMM\", or \"MMMM\"", tok)
|
||||
}
|
||||
case 'D':
|
||||
d := t.Day()
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", d)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", d)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: day of month must either be \"D\" or \"DD\"", tok)
|
||||
}
|
||||
case 'E':
|
||||
d := t.Weekday()
|
||||
switch len(tok) {
|
||||
case 3:
|
||||
buf.WriteString(d.String()[:3])
|
||||
case 4:
|
||||
buf.WriteString(d.String())
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: day of week must either be \"EEE\" or \"EEEE\"", tok)
|
||||
}
|
||||
case 'h':
|
||||
h := t.Hour()
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", h)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", h)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: 24-hour must either be \"h\" or \"hh\"", tok)
|
||||
}
|
||||
case 'H':
|
||||
h := t.Hour() % 12
|
||||
if h == 0 {
|
||||
h = 12
|
||||
}
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", h)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", h)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: 12-hour must either be \"H\" or \"HH\"", tok)
|
||||
}
|
||||
case 'A', 'a':
|
||||
if len(tok) != 2 {
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: must be \"%s%s\"", tok, tok[0:1], tok[0:1])
|
||||
}
|
||||
upper := tok[0] == 'A'
|
||||
switch t.Hour() / 12 {
|
||||
case 0:
|
||||
if upper {
|
||||
buf.WriteString("AM")
|
||||
} else {
|
||||
buf.WriteString("am")
|
||||
}
|
||||
case 1:
|
||||
if upper {
|
||||
buf.WriteString("PM")
|
||||
} else {
|
||||
buf.WriteString("pm")
|
||||
}
|
||||
}
|
||||
case 'm':
|
||||
m := t.Minute()
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", m)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", m)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: minute must either be \"m\" or \"mm\"", tok)
|
||||
}
|
||||
case 's':
|
||||
s := t.Second()
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
fmt.Fprintf(&buf, "%d", s)
|
||||
case 2:
|
||||
fmt.Fprintf(&buf, "%02d", s)
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: second must either be \"s\" or \"ss\"", tok)
|
||||
}
|
||||
case 'Z':
|
||||
// We'll just lean on Go's own formatter for this one, since
|
||||
// the necessary information is unexported.
|
||||
switch len(tok) {
|
||||
case 1:
|
||||
buf.WriteString(t.Format("Z07:00"))
|
||||
case 3:
|
||||
str := t.Format("-0700")
|
||||
switch str {
|
||||
case "+0000":
|
||||
buf.WriteString("UTC")
|
||||
default:
|
||||
buf.WriteString(str)
|
||||
}
|
||||
case 4:
|
||||
buf.WriteString(t.Format("-0700"))
|
||||
case 5:
|
||||
buf.WriteString(t.Format("-07:00"))
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: timezone must be Z, ZZZZ, or ZZZZZ", tok)
|
||||
}
|
||||
default:
|
||||
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q", tok)
|
||||
}
|
||||
|
||||
default:
|
||||
// Any other starting character indicates a literal sequence
|
||||
buf.Write(tok)
|
||||
}
|
||||
}
|
||||
|
||||
return cty.StringVal(buf.String()), nil
|
||||
},
|
||||
})
|
||||
|
||||
// TimeAddFunc is a function that adds a duration to a timestamp, returning a new timestamp.
|
||||
var TimeAddFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "timestamp",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "duration",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
ts, err := parseTimestamp(args[0].AsString())
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
duration, err := time.ParseDuration(args[1].AsString())
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
return cty.StringVal(ts.Add(duration).Format(time.RFC3339)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// FormatDate reformats a timestamp given in RFC3339 syntax into another time
|
||||
// syntax defined by a given format string.
|
||||
//
|
||||
// The format string uses letter mnemonics to represent portions of the
|
||||
// timestamp, with repetition signifying length variants of each portion.
|
||||
// Single quote characters ' can be used to quote sequences of literal letters
|
||||
// that should not be interpreted as formatting mnemonics.
|
||||
//
|
||||
// The full set of supported mnemonic sequences is listed below:
|
||||
//
|
||||
// YY Year modulo 100 zero-padded to two digits, like "06".
|
||||
// YYYY Four (or more) digit year, like "2006".
|
||||
// M Month number, like "1" for January.
|
||||
// MM Month number zero-padded to two digits, like "01".
|
||||
// MMM English month name abbreviated to three letters, like "Jan".
|
||||
// MMMM English month name unabbreviated, like "January".
|
||||
// D Day of month number, like "2".
|
||||
// DD Day of month number zero-padded to two digits, like "02".
|
||||
// EEE English day of week name abbreviated to three letters, like "Mon".
|
||||
// EEEE English day of week name unabbreviated, like "Monday".
|
||||
// h 24-hour number, like "2".
|
||||
// hh 24-hour number zero-padded to two digits, like "02".
|
||||
// H 12-hour number, like "2".
|
||||
// HH 12-hour number zero-padded to two digits, like "02".
|
||||
// AA Hour AM/PM marker in uppercase, like "AM".
|
||||
// aa Hour AM/PM marker in lowercase, like "am".
|
||||
// m Minute within hour, like "5".
|
||||
// mm Minute within hour zero-padded to two digits, like "05".
|
||||
// s Second within minute, like "9".
|
||||
// ss Second within minute zero-padded to two digits, like "09".
|
||||
// ZZZZ Timezone offset with just sign and digit, like "-0800".
|
||||
// ZZZZZ Timezone offset with colon separating hours and minutes, like "-08:00".
|
||||
// Z Like ZZZZZ but with a special case "Z" for UTC.
|
||||
// ZZZ Like ZZZZ but with a special case "UTC" for UTC.
|
||||
//
|
||||
// The format syntax is optimized mainly for generating machine-oriented
|
||||
// timestamps rather than human-oriented timestamps; the English language
|
||||
// portions of the output reflect the use of English names in a number of
|
||||
// machine-readable date formatting standards. For presentation to humans,
|
||||
// a locale-aware time formatter (not included in this package) is a better
|
||||
// choice.
|
||||
//
|
||||
// The format syntax is not compatible with that of any other language, but
|
||||
// is optimized so that patterns for common standard date formats can be
|
||||
// recognized quickly even by a reader unfamiliar with the format syntax.
|
||||
func FormatDate(format cty.Value, timestamp cty.Value) (cty.Value, error) {
|
||||
return FormatDateFunc.Call([]cty.Value{format, timestamp})
|
||||
}
|
||||
|
||||
func parseTimestamp(ts string) (time.Time, error) {
|
||||
t, err := time.Parse(time.RFC3339, ts)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *time.ParseError:
|
||||
// If err is s time.ParseError then its string representation is not
|
||||
// appropriate since it relies on details of Go's strange date format
|
||||
// representation, which a caller of our functions is not expected
|
||||
// to be familiar with.
|
||||
//
|
||||
// Therefore we do some light transformation to get a more suitable
|
||||
// error that should make more sense to our callers. These are
|
||||
// still not awesome error messages, but at least they refer to
|
||||
// the timestamp portions by name rather than by Go's example
|
||||
// values.
|
||||
if err.LayoutElem == "" && err.ValueElem == "" && err.Message != "" {
|
||||
// For some reason err.Message is populated with a ": " prefix
|
||||
// by the time package.
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp%s", err.Message)
|
||||
}
|
||||
var what string
|
||||
switch err.LayoutElem {
|
||||
case "2006":
|
||||
what = "year"
|
||||
case "01":
|
||||
what = "month"
|
||||
case "02":
|
||||
what = "day of month"
|
||||
case "15":
|
||||
what = "hour"
|
||||
case "04":
|
||||
what = "minute"
|
||||
case "05":
|
||||
what = "second"
|
||||
case "Z07:00":
|
||||
what = "UTC offset"
|
||||
case "T":
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: missing required time introducer 'T'")
|
||||
case ":", "-":
|
||||
if err.ValueElem == "" {
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string where %q is expected", err.LayoutElem)
|
||||
} else {
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: found %q where %q is expected", err.ValueElem, err.LayoutElem)
|
||||
}
|
||||
default:
|
||||
// Should never get here, because time.RFC3339 includes only the
|
||||
// above portions, but since that might change in future we'll
|
||||
// be robust here.
|
||||
what = "timestamp segment"
|
||||
}
|
||||
if err.ValueElem == "" {
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string before %s", what)
|
||||
} else {
|
||||
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: cannot use %q as %s", err.ValueElem, what)
|
||||
}
|
||||
}
|
||||
return time.Time{}, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// splitDataFormat is a bufio.SplitFunc used to tokenize a date format.
|
||||
func splitDateFormat(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
const esc = '\''
|
||||
|
||||
switch {
|
||||
|
||||
case data[0] == esc:
|
||||
// If we have another quote immediately after then this is a single
|
||||
// escaped escape.
|
||||
if len(data) > 1 && data[1] == esc {
|
||||
return 2, data[:2], nil
|
||||
}
|
||||
|
||||
// Beginning of quoted sequence, so we will seek forward until we find
|
||||
// the closing quote, ignoring escaped quotes along the way.
|
||||
for i := 1; i < len(data); i++ {
|
||||
if data[i] == esc {
|
||||
if (i + 1) == len(data) {
|
||||
if atEOF {
|
||||
// We have a closing quote and are at the end of our input
|
||||
return len(data), data, nil
|
||||
} else {
|
||||
// We need at least one more byte to decide if this is an
|
||||
// escape or a terminator.
|
||||
return 0, nil, nil
|
||||
}
|
||||
}
|
||||
if data[i+1] == esc {
|
||||
i++ // doubled-up quotes are an escape sequence
|
||||
continue
|
||||
}
|
||||
// We've found the closing quote
|
||||
return i + 1, data[:i+1], nil
|
||||
}
|
||||
}
|
||||
// If we fall out here then we need more bytes to find the end,
|
||||
// unless we're already at the end with an unclosed quote.
|
||||
if atEOF {
|
||||
return len(data), data, nil
|
||||
}
|
||||
return 0, nil, nil
|
||||
|
||||
case startsDateFormatVerb(data[0]):
|
||||
rep := data[0]
|
||||
for i := 1; i < len(data); i++ {
|
||||
if data[i] != rep {
|
||||
return i, data[:i], nil
|
||||
}
|
||||
}
|
||||
if atEOF {
|
||||
return len(data), data, nil
|
||||
}
|
||||
// We need more data to decide if we've found the end
|
||||
return 0, nil, nil
|
||||
|
||||
default:
|
||||
for i := 1; i < len(data); i++ {
|
||||
if data[i] == esc || startsDateFormatVerb(data[i]) {
|
||||
return i, data[:i], nil
|
||||
}
|
||||
}
|
||||
// We might not actually be at the end of a literal sequence,
|
||||
// but that doesn't matter since we'll concat them back together
|
||||
// anyway.
|
||||
return len(data), data, nil
|
||||
}
|
||||
}
|
||||
|
||||
func startsDateFormatVerb(b byte) bool {
|
||||
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')
|
||||
}
|
||||
|
||||
// TimeAdd adds a duration to a timestamp, returning a new timestamp.
|
||||
//
|
||||
// In the HCL language, timestamps are conventionally represented as
|
||||
// strings using RFC 3339 "Date and Time format" syntax. Timeadd requires
|
||||
// the timestamp argument to be a string conforming to this syntax.
|
||||
//
|
||||
// `duration` is a string representation of a time difference, consisting of
|
||||
// sequences of number and unit pairs, like `"1.5h"` or `1h30m`. The accepted
|
||||
// units are `ns`, `us` (or `µs`), `"ms"`, `"s"`, `"m"`, and `"h"`. The first
|
||||
// number may be negative to indicate a negative duration, like `"-2h5m"`.
|
||||
//
|
||||
// The result is a string, also in RFC 3339 format, representing the result
|
||||
// of adding the given direction to the given timestamp.
|
||||
func TimeAdd(timestamp cty.Value, duration cty.Value) (cty.Value, error) {
|
||||
return TimeAddFunc.Call([]cty.Value{timestamp, duration})
|
||||
}
|
13
vendor/github.com/zclconf/go-cty/cty/function/stdlib/doc.go
generated
vendored
Normal file
13
vendor/github.com/zclconf/go-cty/cty/function/stdlib/doc.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// Package stdlib is a collection of cty functions that are expected to be
|
||||
// generally useful, and are thus factored out into this shared library in
|
||||
// the hope that cty-using applications will have consistent behavior when
|
||||
// using these functions.
|
||||
//
|
||||
// See the parent package "function" for more information on the purpose
|
||||
// and usage of cty functions.
|
||||
//
|
||||
// This package contains both Go functions, which provide convenient access
|
||||
// to call the functions from Go code, and the Function objects themselves.
|
||||
// The latter follow the naming scheme of appending "Func" to the end of
|
||||
// the function name.
|
||||
package stdlib
|
519
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go
generated
vendored
Normal file
519
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go
generated
vendored
Normal file
@ -0,0 +1,519 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/apparentlymart/go-textseg/v13/textseg"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/json"
|
||||
)
|
||||
|
||||
//go:generate ragel -Z format_fsm.rl
|
||||
//go:generate gofmt -w format_fsm.go
|
||||
|
||||
var FormatFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "format",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "args",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowNull: true,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
for _, arg := range args[1:] {
|
||||
if !arg.IsWhollyKnown() {
|
||||
// We require all nested values to be known because the only
|
||||
// thing we can do for a collection/structural type is print
|
||||
// it as JSON and that requires it to be wholly known.
|
||||
return cty.UnknownVal(cty.String), nil
|
||||
}
|
||||
}
|
||||
str, err := formatFSM(args[0].AsString(), args[1:])
|
||||
return cty.StringVal(str), err
|
||||
},
|
||||
})
|
||||
|
||||
var FormatListFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "format",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "args",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowNull: true,
|
||||
AllowUnknown: true,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.String)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
fmtVal := args[0]
|
||||
args = args[1:]
|
||||
|
||||
if len(args) == 0 {
|
||||
// With no arguments, this function is equivalent to Format, but
|
||||
// returning a single-element list result.
|
||||
result, err := Format(fmtVal, args...)
|
||||
return cty.ListVal([]cty.Value{result}), err
|
||||
}
|
||||
|
||||
fmtStr := fmtVal.AsString()
|
||||
|
||||
// Each of our arguments will be dealt with either as an iterator
|
||||
// or as a single value. Iterators are used for sequence-type values
|
||||
// (lists, sets, tuples) while everything else is treated as a
|
||||
// single value. The sequences we iterate over are required to be
|
||||
// all the same length.
|
||||
iterLen := -1
|
||||
lenChooser := -1
|
||||
iterators := make([]cty.ElementIterator, len(args))
|
||||
singleVals := make([]cty.Value, len(args))
|
||||
unknowns := make([]bool, len(args))
|
||||
for i, arg := range args {
|
||||
argTy := arg.Type()
|
||||
switch {
|
||||
case (argTy.IsListType() || argTy.IsSetType() || argTy.IsTupleType()) && !arg.IsNull():
|
||||
if !argTy.IsTupleType() && !(arg.IsKnown() && arg.Length().IsKnown()) {
|
||||
// We can't iterate this one at all yet then, so we can't
|
||||
// yet produce a result.
|
||||
unknowns[i] = true
|
||||
continue
|
||||
}
|
||||
thisLen := arg.LengthInt()
|
||||
if iterLen == -1 {
|
||||
iterLen = thisLen
|
||||
lenChooser = i
|
||||
} else {
|
||||
if thisLen != iterLen {
|
||||
return cty.NullVal(cty.List(cty.String)), function.NewArgErrorf(
|
||||
i+1,
|
||||
"argument %d has length %d, which is inconsistent with argument %d of length %d",
|
||||
i+1, thisLen,
|
||||
lenChooser+1, iterLen,
|
||||
)
|
||||
}
|
||||
}
|
||||
if !arg.IsKnown() {
|
||||
// We allowed an unknown tuple value to fall through in
|
||||
// our initial check above so that we'd be able to run
|
||||
// the above error checks against it, but we still can't
|
||||
// iterate it if the checks pass.
|
||||
unknowns[i] = true
|
||||
continue
|
||||
}
|
||||
iterators[i] = arg.ElementIterator()
|
||||
case arg == cty.DynamicVal:
|
||||
unknowns[i] = true
|
||||
default:
|
||||
singleVals[i] = arg
|
||||
}
|
||||
}
|
||||
|
||||
for _, isUnk := range unknowns {
|
||||
if isUnk {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
}
|
||||
|
||||
if iterLen == 0 {
|
||||
// If our sequences are all empty then our result must be empty.
|
||||
return cty.ListValEmpty(cty.String), nil
|
||||
}
|
||||
|
||||
if iterLen == -1 {
|
||||
// If we didn't encounter any iterables at all then we're going
|
||||
// to just do one iteration with items from singleVals.
|
||||
iterLen = 1
|
||||
}
|
||||
|
||||
ret := make([]cty.Value, 0, iterLen)
|
||||
fmtArgs := make([]cty.Value, len(iterators))
|
||||
Results:
|
||||
for iterIdx := 0; iterIdx < iterLen; iterIdx++ {
|
||||
|
||||
// Construct our arguments for a single format call
|
||||
for i := range fmtArgs {
|
||||
switch {
|
||||
case iterators[i] != nil:
|
||||
iterator := iterators[i]
|
||||
iterator.Next()
|
||||
_, val := iterator.Element()
|
||||
fmtArgs[i] = val
|
||||
default:
|
||||
fmtArgs[i] = singleVals[i]
|
||||
}
|
||||
|
||||
// If any of the arguments to this call would be unknown then
|
||||
// this particular result is unknown, but we'll keep going
|
||||
// to see if any other iterations can produce known values.
|
||||
if !fmtArgs[i].IsWhollyKnown() {
|
||||
// We require all nested values to be known because the only
|
||||
// thing we can do for a collection/structural type is print
|
||||
// it as JSON and that requires it to be wholly known.
|
||||
ret = append(ret, cty.UnknownVal(cty.String))
|
||||
continue Results
|
||||
}
|
||||
}
|
||||
|
||||
str, err := formatFSM(fmtStr, fmtArgs)
|
||||
if err != nil {
|
||||
return cty.NullVal(cty.List(cty.String)), fmt.Errorf(
|
||||
"error on format iteration %d: %s", iterIdx, err,
|
||||
)
|
||||
}
|
||||
|
||||
ret = append(ret, cty.StringVal(str))
|
||||
}
|
||||
|
||||
return cty.ListVal(ret), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Format produces a string representation of zero or more values using a
|
||||
// format string similar to the "printf" function in C.
|
||||
//
|
||||
// It supports the following "verbs":
|
||||
//
|
||||
// %% Literal percent sign, consuming no value
|
||||
// %v A default formatting of the value based on type, as described below.
|
||||
// %#v JSON serialization of the value
|
||||
// %t Converts to boolean and then produces "true" or "false"
|
||||
// %b Converts to number, requires integer, produces binary representation
|
||||
// %d Converts to number, requires integer, produces decimal representation
|
||||
// %o Converts to number, requires integer, produces octal representation
|
||||
// %x Converts to number, requires integer, produces hexadecimal representation
|
||||
// with lowercase letters
|
||||
// %X Like %x but with uppercase letters
|
||||
// %e Converts to number, produces scientific notation like -1.234456e+78
|
||||
// %E Like %e but with an uppercase "E" representing the exponent
|
||||
// %f Converts to number, produces decimal representation with fractional
|
||||
// part but no exponent, like 123.456
|
||||
// %g %e for large exponents or %f otherwise
|
||||
// %G %E for large exponents or %f otherwise
|
||||
// %s Converts to string and produces the string's characters
|
||||
// %q Converts to string and produces JSON-quoted string representation,
|
||||
// like %v.
|
||||
//
|
||||
// The default format selections made by %v are:
|
||||
//
|
||||
// string %s
|
||||
// number %g
|
||||
// bool %t
|
||||
// other %#v
|
||||
//
|
||||
// Null values produce the literal keyword "null" for %v and %#v, and produce
|
||||
// an error otherwise.
|
||||
//
|
||||
// Width is specified by an optional decimal number immediately preceding the
|
||||
// verb letter. If absent, the width is whatever is necessary to represent the
|
||||
// value. Precision is specified after the (optional) width by a period
|
||||
// followed by a decimal number. If no period is present, a default precision
|
||||
// is used. A period with no following number is invalid.
|
||||
// For examples:
|
||||
//
|
||||
// %f default width, default precision
|
||||
// %9f width 9, default precision
|
||||
// %.2f default width, precision 2
|
||||
// %9.2f width 9, precision 2
|
||||
//
|
||||
// Width and precision are measured in unicode characters (grapheme clusters).
|
||||
//
|
||||
// For most values, width is the minimum number of characters to output,
|
||||
// padding the formatted form with spaces if necessary.
|
||||
//
|
||||
// For strings, precision limits the length of the input to be formatted (not
|
||||
// the size of the output), truncating if necessary.
|
||||
//
|
||||
// For numbers, width sets the minimum width of the field and precision sets
|
||||
// the number of places after the decimal, if appropriate, except that for
|
||||
// %g/%G precision sets the total number of significant digits.
|
||||
//
|
||||
// The following additional symbols can be used immediately after the percent
|
||||
// introducer as flags:
|
||||
//
|
||||
// (a space) leave a space where the sign would be if number is positive
|
||||
// + Include a sign for a number even if it is positive (numeric only)
|
||||
// - Pad with spaces on the left rather than the right
|
||||
// 0 Pad with zeros rather than spaces.
|
||||
//
|
||||
// Flag characters are ignored for verbs that do not support them.
|
||||
//
|
||||
// By default, % sequences consume successive arguments starting with the first.
|
||||
// Introducing a [n] sequence immediately before the verb letter, where n is a
|
||||
// decimal integer, explicitly chooses a particular value argument by its
|
||||
// one-based index. Subsequent calls without an explicit index will then
|
||||
// proceed with n+1, n+2, etc.
|
||||
//
|
||||
// An error is produced if the format string calls for an impossible conversion
|
||||
// or accesses more values than are given. An error is produced also for
|
||||
// an unsupported format verb.
|
||||
func Format(format cty.Value, vals ...cty.Value) (cty.Value, error) {
|
||||
args := make([]cty.Value, 0, len(vals)+1)
|
||||
args = append(args, format)
|
||||
args = append(args, vals...)
|
||||
return FormatFunc.Call(args)
|
||||
}
|
||||
|
||||
// FormatList applies the same formatting behavior as Format, but accepts
|
||||
// a mixture of list and non-list values as arguments. Any list arguments
|
||||
// passed must have the same length, which dictates the length of the
|
||||
// resulting list.
|
||||
//
|
||||
// Any non-list arguments are used repeatedly for each iteration over the
|
||||
// list arguments. The list arguments are iterated in order by key, so
|
||||
// corresponding items are formatted together.
|
||||
func FormatList(format cty.Value, vals ...cty.Value) (cty.Value, error) {
|
||||
args := make([]cty.Value, 0, len(vals)+1)
|
||||
args = append(args, format)
|
||||
args = append(args, vals...)
|
||||
return FormatListFunc.Call(args)
|
||||
}
|
||||
|
||||
type formatVerb struct {
|
||||
Raw string
|
||||
Offset int
|
||||
|
||||
ArgNum int
|
||||
Mode rune
|
||||
|
||||
Zero bool
|
||||
Sharp bool
|
||||
Plus bool
|
||||
Minus bool
|
||||
Space bool
|
||||
|
||||
HasPrec bool
|
||||
Prec int
|
||||
|
||||
HasWidth bool
|
||||
Width int
|
||||
}
|
||||
|
||||
// formatAppend is called by formatFSM (generated by format_fsm.rl) for each
|
||||
// formatting sequence that is encountered.
|
||||
func formatAppend(verb *formatVerb, buf *bytes.Buffer, args []cty.Value) error {
|
||||
argIdx := verb.ArgNum - 1
|
||||
if argIdx >= len(args) {
|
||||
return fmt.Errorf(
|
||||
"not enough arguments for %q at %d: need index %d but have %d total",
|
||||
verb.Raw, verb.Offset,
|
||||
verb.ArgNum, len(args),
|
||||
)
|
||||
}
|
||||
arg := args[argIdx]
|
||||
|
||||
if verb.Mode != 'v' && arg.IsNull() {
|
||||
return fmt.Errorf("unsupported value for %q at %d: null value cannot be formatted", verb.Raw, verb.Offset)
|
||||
}
|
||||
|
||||
// Normalize to make some things easier for downstream formatters
|
||||
if !verb.HasWidth {
|
||||
verb.Width = -1
|
||||
}
|
||||
if !verb.HasPrec {
|
||||
verb.Prec = -1
|
||||
}
|
||||
|
||||
// For our first pass we'll ensure the verb is supported and then fan
|
||||
// out to other functions based on what conversion is needed.
|
||||
switch verb.Mode {
|
||||
|
||||
case 'v':
|
||||
return formatAppendAsIs(verb, buf, arg)
|
||||
|
||||
case 't':
|
||||
return formatAppendBool(verb, buf, arg)
|
||||
|
||||
case 'b', 'd', 'o', 'x', 'X', 'e', 'E', 'f', 'g', 'G':
|
||||
return formatAppendNumber(verb, buf, arg)
|
||||
|
||||
case 's', 'q':
|
||||
return formatAppendString(verb, buf, arg)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported format verb %q in %q at offset %d", verb.Mode, verb.Raw, verb.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
func formatAppendAsIs(verb *formatVerb, buf *bytes.Buffer, arg cty.Value) error {
|
||||
|
||||
if !verb.Sharp && !arg.IsNull() {
|
||||
// Unless the caller overrode it with the sharp flag, we'll try some
|
||||
// specialized formats before we fall back on JSON.
|
||||
switch arg.Type() {
|
||||
case cty.String:
|
||||
fmted := arg.AsString()
|
||||
fmted = formatPadWidth(verb, fmted)
|
||||
buf.WriteString(fmted)
|
||||
return nil
|
||||
case cty.Number:
|
||||
bf := arg.AsBigFloat()
|
||||
fmted := bf.Text('g', -1)
|
||||
fmted = formatPadWidth(verb, fmted)
|
||||
buf.WriteString(fmted)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
jb, err := json.Marshal(arg, arg.Type())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unsupported value for %q at %d: %s", verb.Raw, verb.Offset, err)
|
||||
}
|
||||
fmted := formatPadWidth(verb, string(jb))
|
||||
buf.WriteString(fmted)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatAppendBool(verb *formatVerb, buf *bytes.Buffer, arg cty.Value) error {
|
||||
var err error
|
||||
arg, err = convert.Convert(arg, cty.Bool)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unsupported value for %q at %d: %s", verb.Raw, verb.Offset, err)
|
||||
}
|
||||
|
||||
if arg.True() {
|
||||
buf.WriteString("true")
|
||||
} else {
|
||||
buf.WriteString("false")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatAppendNumber(verb *formatVerb, buf *bytes.Buffer, arg cty.Value) error {
|
||||
var err error
|
||||
arg, err = convert.Convert(arg, cty.Number)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unsupported value for %q at %d: %s", verb.Raw, verb.Offset, err)
|
||||
}
|
||||
|
||||
switch verb.Mode {
|
||||
case 'b', 'd', 'o', 'x', 'X':
|
||||
return formatAppendInteger(verb, buf, arg)
|
||||
default:
|
||||
bf := arg.AsBigFloat()
|
||||
|
||||
// For floats our format syntax is a subset of Go's, so it's
|
||||
// safe for us to just lean on the existing Go implementation.
|
||||
fmtstr := formatStripIndexSegment(verb.Raw)
|
||||
fmted := fmt.Sprintf(fmtstr, bf)
|
||||
buf.WriteString(fmted)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func formatAppendInteger(verb *formatVerb, buf *bytes.Buffer, arg cty.Value) error {
|
||||
bf := arg.AsBigFloat()
|
||||
bi, acc := bf.Int(nil)
|
||||
if acc != big.Exact {
|
||||
return fmt.Errorf("unsupported value for %q at %d: an integer is required", verb.Raw, verb.Offset)
|
||||
}
|
||||
|
||||
// For integers our format syntax is a subset of Go's, so it's
|
||||
// safe for us to just lean on the existing Go implementation.
|
||||
fmtstr := formatStripIndexSegment(verb.Raw)
|
||||
fmted := fmt.Sprintf(fmtstr, bi)
|
||||
buf.WriteString(fmted)
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatAppendString(verb *formatVerb, buf *bytes.Buffer, arg cty.Value) error {
|
||||
var err error
|
||||
arg, err = convert.Convert(arg, cty.String)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unsupported value for %q at %d: %s", verb.Raw, verb.Offset, err)
|
||||
}
|
||||
|
||||
// We _cannot_ directly use the Go fmt.Sprintf implementation for strings
|
||||
// because it measures widths and precisions in runes rather than grapheme
|
||||
// clusters.
|
||||
|
||||
str := arg.AsString()
|
||||
if verb.Prec > 0 {
|
||||
strB := []byte(str)
|
||||
pos := 0
|
||||
wanted := verb.Prec
|
||||
for i := 0; i < wanted; i++ {
|
||||
next := strB[pos:]
|
||||
if len(next) == 0 {
|
||||
// ran out of characters before we hit our max width
|
||||
break
|
||||
}
|
||||
d, _, _ := textseg.ScanGraphemeClusters(strB[pos:], true)
|
||||
pos += d
|
||||
}
|
||||
str = str[:pos]
|
||||
}
|
||||
|
||||
switch verb.Mode {
|
||||
case 's':
|
||||
fmted := formatPadWidth(verb, str)
|
||||
buf.WriteString(fmted)
|
||||
case 'q':
|
||||
jb, err := json.Marshal(cty.StringVal(str), cty.String)
|
||||
if err != nil {
|
||||
// Should never happen, since we know this is a known, non-null string
|
||||
panic(fmt.Errorf("failed to marshal %#v as JSON: %s", arg, err))
|
||||
}
|
||||
fmted := formatPadWidth(verb, string(jb))
|
||||
buf.WriteString(fmted)
|
||||
default:
|
||||
// Should never happen because formatAppend should've already validated
|
||||
panic(fmt.Errorf("invalid string formatting mode %q", verb.Mode))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatPadWidth(verb *formatVerb, fmted string) string {
|
||||
if verb.Width < 0 {
|
||||
return fmted
|
||||
}
|
||||
|
||||
// Safe to ignore errors because ScanGraphemeClusters cannot produce errors
|
||||
givenLen, _ := textseg.TokenCount([]byte(fmted), textseg.ScanGraphemeClusters)
|
||||
wantLen := verb.Width
|
||||
if givenLen >= wantLen {
|
||||
return fmted
|
||||
}
|
||||
|
||||
padLen := wantLen - givenLen
|
||||
padChar := " "
|
||||
if verb.Zero {
|
||||
padChar = "0"
|
||||
}
|
||||
pads := strings.Repeat(padChar, padLen)
|
||||
|
||||
if verb.Minus {
|
||||
return fmted + pads
|
||||
}
|
||||
return pads + fmted
|
||||
}
|
||||
|
||||
// formatStripIndexSegment strips out any [nnn] segment present in a verb
|
||||
// string so that we can pass it through to Go's fmt.Sprintf with a single
|
||||
// argument. This is used in cases where we're just leaning on Go's formatter
|
||||
// because it's a superset of ours.
|
||||
func formatStripIndexSegment(rawVerb string) string {
|
||||
// We assume the string has already been validated here, since we should
|
||||
// only be using this function with strings that were accepted by our
|
||||
// scanner in formatFSM.
|
||||
start := strings.Index(rawVerb, "[")
|
||||
end := strings.Index(rawVerb, "]")
|
||||
if start == -1 || end == -1 {
|
||||
return rawVerb
|
||||
}
|
||||
|
||||
return rawVerb[:start] + rawVerb[end+1:]
|
||||
}
|
374
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.go
generated
vendored
Normal file
374
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.go
generated
vendored
Normal file
@ -0,0 +1,374 @@
|
||||
// line 1 "format_fsm.rl"
|
||||
// This file is generated from format_fsm.rl. DO NOT EDIT.
|
||||
|
||||
// line 5 "format_fsm.rl"
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// line 21 "format_fsm.go"
|
||||
var _formatfsm_actions []byte = []byte{
|
||||
0, 1, 0, 1, 1, 1, 2, 1, 4,
|
||||
1, 5, 1, 6, 1, 7, 1, 8,
|
||||
1, 9, 1, 10, 1, 11, 1, 14,
|
||||
1, 16, 1, 17, 1, 18, 2, 3,
|
||||
4, 2, 12, 10, 2, 12, 16, 2,
|
||||
12, 18, 2, 13, 14, 2, 15, 10,
|
||||
2, 15, 18,
|
||||
}
|
||||
|
||||
var _formatfsm_key_offsets []byte = []byte{
|
||||
0, 0, 14, 27, 34, 36, 39, 43,
|
||||
51,
|
||||
}
|
||||
|
||||
var _formatfsm_trans_keys []byte = []byte{
|
||||
32, 35, 37, 43, 45, 46, 48, 91,
|
||||
49, 57, 65, 90, 97, 122, 32, 35,
|
||||
43, 45, 46, 48, 91, 49, 57, 65,
|
||||
90, 97, 122, 91, 48, 57, 65, 90,
|
||||
97, 122, 49, 57, 93, 48, 57, 65,
|
||||
90, 97, 122, 46, 91, 48, 57, 65,
|
||||
90, 97, 122, 37,
|
||||
}
|
||||
|
||||
var _formatfsm_single_lengths []byte = []byte{
|
||||
0, 8, 7, 1, 0, 1, 0, 2,
|
||||
1,
|
||||
}
|
||||
|
||||
var _formatfsm_range_lengths []byte = []byte{
|
||||
0, 3, 3, 3, 1, 1, 2, 3,
|
||||
0,
|
||||
}
|
||||
|
||||
var _formatfsm_index_offsets []byte = []byte{
|
||||
0, 0, 12, 23, 28, 30, 33, 36,
|
||||
42,
|
||||
}
|
||||
|
||||
var _formatfsm_indicies []byte = []byte{
|
||||
1, 2, 3, 4, 5, 6, 7, 10,
|
||||
8, 9, 9, 0, 1, 2, 4, 5,
|
||||
6, 7, 10, 8, 9, 9, 0, 13,
|
||||
11, 12, 12, 0, 14, 0, 15, 14,
|
||||
0, 9, 9, 0, 16, 19, 17, 18,
|
||||
18, 0, 20, 3,
|
||||
}
|
||||
|
||||
var _formatfsm_trans_targs []byte = []byte{
|
||||
0, 2, 2, 8, 2, 2, 3, 2,
|
||||
7, 8, 4, 3, 8, 4, 5, 6,
|
||||
3, 7, 8, 4, 1,
|
||||
}
|
||||
|
||||
var _formatfsm_trans_actions []byte = []byte{
|
||||
7, 17, 9, 3, 15, 13, 25, 11,
|
||||
43, 29, 19, 27, 49, 46, 21, 0,
|
||||
37, 23, 40, 34, 1,
|
||||
}
|
||||
|
||||
var _formatfsm_eof_actions []byte = []byte{
|
||||
0, 31, 31, 31, 31, 31, 31, 31,
|
||||
5,
|
||||
}
|
||||
|
||||
const formatfsm_start int = 8
|
||||
const formatfsm_first_final int = 8
|
||||
const formatfsm_error int = 0
|
||||
|
||||
const formatfsm_en_main int = 8
|
||||
|
||||
// line 20 "format_fsm.rl"
|
||||
|
||||
func formatFSM(format string, a []cty.Value) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
data := format
|
||||
nextArg := 1 // arg numbers are 1-based
|
||||
var verb formatVerb
|
||||
highestArgIdx := 0 // zero means "none", since arg numbers are 1-based
|
||||
|
||||
// line 159 "format_fsm.rl"
|
||||
|
||||
// Ragel state
|
||||
p := 0 // "Pointer" into data
|
||||
pe := len(data) // End-of-data "pointer"
|
||||
cs := 0 // current state (will be initialized by ragel-generated code)
|
||||
ts := 0
|
||||
te := 0
|
||||
eof := pe
|
||||
|
||||
// Keep Go compiler happy even if generated code doesn't use these
|
||||
_ = ts
|
||||
_ = te
|
||||
_ = eof
|
||||
|
||||
// line 123 "format_fsm.go"
|
||||
{
|
||||
cs = formatfsm_start
|
||||
}
|
||||
|
||||
// line 128 "format_fsm.go"
|
||||
{
|
||||
var _klen int
|
||||
var _trans int
|
||||
var _acts int
|
||||
var _nacts uint
|
||||
var _keys int
|
||||
if p == pe {
|
||||
goto _test_eof
|
||||
}
|
||||
if cs == 0 {
|
||||
goto _out
|
||||
}
|
||||
_resume:
|
||||
_keys = int(_formatfsm_key_offsets[cs])
|
||||
_trans = int(_formatfsm_index_offsets[cs])
|
||||
|
||||
_klen = int(_formatfsm_single_lengths[cs])
|
||||
if _klen > 0 {
|
||||
_lower := int(_keys)
|
||||
var _mid int
|
||||
_upper := int(_keys + _klen - 1)
|
||||
for {
|
||||
if _upper < _lower {
|
||||
break
|
||||
}
|
||||
|
||||
_mid = _lower + ((_upper - _lower) >> 1)
|
||||
switch {
|
||||
case data[p] < _formatfsm_trans_keys[_mid]:
|
||||
_upper = _mid - 1
|
||||
case data[p] > _formatfsm_trans_keys[_mid]:
|
||||
_lower = _mid + 1
|
||||
default:
|
||||
_trans += int(_mid - int(_keys))
|
||||
goto _match
|
||||
}
|
||||
}
|
||||
_keys += _klen
|
||||
_trans += _klen
|
||||
}
|
||||
|
||||
_klen = int(_formatfsm_range_lengths[cs])
|
||||
if _klen > 0 {
|
||||
_lower := int(_keys)
|
||||
var _mid int
|
||||
_upper := int(_keys + (_klen << 1) - 2)
|
||||
for {
|
||||
if _upper < _lower {
|
||||
break
|
||||
}
|
||||
|
||||
_mid = _lower + (((_upper - _lower) >> 1) & ^1)
|
||||
switch {
|
||||
case data[p] < _formatfsm_trans_keys[_mid]:
|
||||
_upper = _mid - 2
|
||||
case data[p] > _formatfsm_trans_keys[_mid+1]:
|
||||
_lower = _mid + 2
|
||||
default:
|
||||
_trans += int((_mid - int(_keys)) >> 1)
|
||||
goto _match
|
||||
}
|
||||
}
|
||||
_trans += _klen
|
||||
}
|
||||
|
||||
_match:
|
||||
_trans = int(_formatfsm_indicies[_trans])
|
||||
cs = int(_formatfsm_trans_targs[_trans])
|
||||
|
||||
if _formatfsm_trans_actions[_trans] == 0 {
|
||||
goto _again
|
||||
}
|
||||
|
||||
_acts = int(_formatfsm_trans_actions[_trans])
|
||||
_nacts = uint(_formatfsm_actions[_acts])
|
||||
_acts++
|
||||
for ; _nacts > 0; _nacts-- {
|
||||
_acts++
|
||||
switch _formatfsm_actions[_acts-1] {
|
||||
case 0:
|
||||
// line 31 "format_fsm.rl"
|
||||
|
||||
verb = formatVerb{
|
||||
ArgNum: nextArg,
|
||||
Prec: -1,
|
||||
Width: -1,
|
||||
}
|
||||
ts = p
|
||||
|
||||
case 1:
|
||||
// line 40 "format_fsm.rl"
|
||||
|
||||
buf.WriteByte(data[p])
|
||||
|
||||
case 4:
|
||||
// line 51 "format_fsm.rl"
|
||||
|
||||
// We'll try to slurp a whole UTF-8 sequence here, to give the user
|
||||
// better feedback.
|
||||
r, _ := utf8.DecodeRuneInString(data[p:])
|
||||
return buf.String(), fmt.Errorf("unrecognized format character %q at offset %d", r, p)
|
||||
|
||||
case 5:
|
||||
// line 58 "format_fsm.rl"
|
||||
|
||||
verb.Sharp = true
|
||||
|
||||
case 6:
|
||||
// line 61 "format_fsm.rl"
|
||||
|
||||
verb.Zero = true
|
||||
|
||||
case 7:
|
||||
// line 64 "format_fsm.rl"
|
||||
|
||||
verb.Minus = true
|
||||
|
||||
case 8:
|
||||
// line 67 "format_fsm.rl"
|
||||
|
||||
verb.Plus = true
|
||||
|
||||
case 9:
|
||||
// line 70 "format_fsm.rl"
|
||||
|
||||
verb.Space = true
|
||||
|
||||
case 10:
|
||||
// line 74 "format_fsm.rl"
|
||||
|
||||
verb.ArgNum = 0
|
||||
|
||||
case 11:
|
||||
// line 77 "format_fsm.rl"
|
||||
|
||||
verb.ArgNum = (10 * verb.ArgNum) + (int(data[p]) - '0')
|
||||
|
||||
case 12:
|
||||
// line 81 "format_fsm.rl"
|
||||
|
||||
verb.HasWidth = true
|
||||
|
||||
case 13:
|
||||
// line 84 "format_fsm.rl"
|
||||
|
||||
verb.Width = 0
|
||||
|
||||
case 14:
|
||||
// line 87 "format_fsm.rl"
|
||||
|
||||
verb.Width = (10 * verb.Width) + (int(data[p]) - '0')
|
||||
|
||||
case 15:
|
||||
// line 91 "format_fsm.rl"
|
||||
|
||||
verb.HasPrec = true
|
||||
|
||||
case 16:
|
||||
// line 94 "format_fsm.rl"
|
||||
|
||||
verb.Prec = 0
|
||||
|
||||
case 17:
|
||||
// line 97 "format_fsm.rl"
|
||||
|
||||
verb.Prec = (10 * verb.Prec) + (int(data[p]) - '0')
|
||||
|
||||
case 18:
|
||||
// line 101 "format_fsm.rl"
|
||||
|
||||
verb.Mode = rune(data[p])
|
||||
te = p + 1
|
||||
verb.Raw = data[ts:te]
|
||||
verb.Offset = ts
|
||||
|
||||
if verb.ArgNum > highestArgIdx {
|
||||
highestArgIdx = verb.ArgNum
|
||||
}
|
||||
|
||||
err := formatAppend(&verb, &buf, a)
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
nextArg = verb.ArgNum + 1
|
||||
|
||||
// line 330 "format_fsm.go"
|
||||
}
|
||||
}
|
||||
|
||||
_again:
|
||||
if cs == 0 {
|
||||
goto _out
|
||||
}
|
||||
p++
|
||||
if p != pe {
|
||||
goto _resume
|
||||
}
|
||||
_test_eof:
|
||||
{
|
||||
}
|
||||
if p == eof {
|
||||
__acts := _formatfsm_eof_actions[cs]
|
||||
__nacts := uint(_formatfsm_actions[__acts])
|
||||
__acts++
|
||||
for ; __nacts > 0; __nacts-- {
|
||||
__acts++
|
||||
switch _formatfsm_actions[__acts-1] {
|
||||
case 2:
|
||||
// line 44 "format_fsm.rl"
|
||||
|
||||
case 3:
|
||||
// line 47 "format_fsm.rl"
|
||||
|
||||
return buf.String(), fmt.Errorf("invalid format string starting at offset %d", p)
|
||||
|
||||
case 4:
|
||||
// line 51 "format_fsm.rl"
|
||||
|
||||
// We'll try to slurp a whole UTF-8 sequence here, to give the user
|
||||
// better feedback.
|
||||
r, _ := utf8.DecodeRuneInString(data[p:])
|
||||
return buf.String(), fmt.Errorf("unrecognized format character %q at offset %d", r, p)
|
||||
|
||||
// line 369 "format_fsm.go"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_out:
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// line 177 "format_fsm.rl"
|
||||
|
||||
// If we fall out here without being in a final state then we've
|
||||
// encountered something that the scanner can't match, which should
|
||||
// be impossible (the scanner matches all bytes _somehow_) but we'll
|
||||
// flag it anyway rather than just losing data from the end.
|
||||
if cs < formatfsm_first_final {
|
||||
return buf.String(), fmt.Errorf("extraneous characters beginning at offset %d", p)
|
||||
}
|
||||
|
||||
if highestArgIdx < len(a) {
|
||||
// Extraneous args are an error, to more easily detect mistakes
|
||||
firstBad := highestArgIdx + 1
|
||||
if highestArgIdx == 0 {
|
||||
// Custom error message for this case
|
||||
return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; no verbs in format string")
|
||||
}
|
||||
return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; only %d used by format string", highestArgIdx)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
198
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.rl
generated
vendored
Normal file
198
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.rl
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
// This file is generated from format_fsm.rl. DO NOT EDIT.
|
||||
%%{
|
||||
# (except you are actually in scan_tokens.rl here, so edit away!)
|
||||
machine formatfsm;
|
||||
}%%
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
%%{
|
||||
write data;
|
||||
}%%
|
||||
|
||||
func formatFSM(format string, a []cty.Value) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
data := format
|
||||
nextArg := 1 // arg numbers are 1-based
|
||||
var verb formatVerb
|
||||
highestArgIdx := 0 // zero means "none", since arg numbers are 1-based
|
||||
|
||||
%%{
|
||||
|
||||
action begin {
|
||||
verb = formatVerb{
|
||||
ArgNum: nextArg,
|
||||
Prec: -1,
|
||||
Width: -1,
|
||||
}
|
||||
ts = p
|
||||
}
|
||||
|
||||
action emit {
|
||||
buf.WriteByte(fc);
|
||||
}
|
||||
|
||||
action finish_ok {
|
||||
}
|
||||
|
||||
action finish_err {
|
||||
return buf.String(), fmt.Errorf("invalid format string starting at offset %d", p)
|
||||
}
|
||||
|
||||
action err_char {
|
||||
// We'll try to slurp a whole UTF-8 sequence here, to give the user
|
||||
// better feedback.
|
||||
r, _ := utf8.DecodeRuneInString(data[p:])
|
||||
return buf.String(), fmt.Errorf("unrecognized format character %q at offset %d", r, p)
|
||||
}
|
||||
|
||||
action flag_sharp {
|
||||
verb.Sharp = true
|
||||
}
|
||||
action flag_zero {
|
||||
verb.Zero = true
|
||||
}
|
||||
action flag_minus {
|
||||
verb.Minus = true
|
||||
}
|
||||
action flag_plus {
|
||||
verb.Plus = true
|
||||
}
|
||||
action flag_space {
|
||||
verb.Space = true
|
||||
}
|
||||
|
||||
action argidx_reset {
|
||||
verb.ArgNum = 0
|
||||
}
|
||||
action argidx_num {
|
||||
verb.ArgNum = (10 * verb.ArgNum) + (int(fc) - '0')
|
||||
}
|
||||
|
||||
action has_width {
|
||||
verb.HasWidth = true
|
||||
}
|
||||
action width_reset {
|
||||
verb.Width = 0
|
||||
}
|
||||
action width_num {
|
||||
verb.Width = (10 * verb.Width) + (int(fc) - '0')
|
||||
}
|
||||
|
||||
action has_prec {
|
||||
verb.HasPrec = true
|
||||
}
|
||||
action prec_reset {
|
||||
verb.Prec = 0
|
||||
}
|
||||
action prec_num {
|
||||
verb.Prec = (10 * verb.Prec) + (int(fc) - '0')
|
||||
}
|
||||
|
||||
action mode {
|
||||
verb.Mode = rune(fc)
|
||||
te = p+1
|
||||
verb.Raw = data[ts:te]
|
||||
verb.Offset = ts
|
||||
|
||||
if verb.ArgNum > highestArgIdx {
|
||||
highestArgIdx = verb.ArgNum
|
||||
}
|
||||
|
||||
err := formatAppend(&verb, &buf, a)
|
||||
if err != nil {
|
||||
return buf.String(), err
|
||||
}
|
||||
nextArg = verb.ArgNum + 1
|
||||
}
|
||||
|
||||
# a number that isn't zero and doesn't have a leading zero
|
||||
num = [1-9] [0-9]*;
|
||||
|
||||
flags = (
|
||||
'0' @flag_zero |
|
||||
'#' @flag_sharp |
|
||||
'-' @flag_minus |
|
||||
'+' @flag_plus |
|
||||
' ' @flag_space
|
||||
)*;
|
||||
|
||||
argidx = ((
|
||||
'[' (num $argidx_num) ']'
|
||||
) >argidx_reset)?;
|
||||
|
||||
width = (
|
||||
( num $width_num ) >width_reset %has_width
|
||||
)?;
|
||||
|
||||
precision = (
|
||||
('.' ( digit* $prec_num )) >prec_reset %has_prec
|
||||
)?;
|
||||
|
||||
# We accept any letter here, but will be more picky in formatAppend
|
||||
mode = ('a'..'z' | 'A'..'Z') @mode;
|
||||
|
||||
fmt_verb = (
|
||||
'%' @begin
|
||||
flags
|
||||
width
|
||||
precision
|
||||
argidx
|
||||
mode
|
||||
);
|
||||
|
||||
main := (
|
||||
[^%] @emit |
|
||||
'%%' @emit |
|
||||
fmt_verb
|
||||
)* @/finish_err %/finish_ok $!err_char;
|
||||
|
||||
}%%
|
||||
|
||||
// Ragel state
|
||||
p := 0 // "Pointer" into data
|
||||
pe := len(data) // End-of-data "pointer"
|
||||
cs := 0 // current state (will be initialized by ragel-generated code)
|
||||
ts := 0
|
||||
te := 0
|
||||
eof := pe
|
||||
|
||||
// Keep Go compiler happy even if generated code doesn't use these
|
||||
_ = ts
|
||||
_ = te
|
||||
_ = eof
|
||||
|
||||
%%{
|
||||
write init;
|
||||
write exec;
|
||||
}%%
|
||||
|
||||
// If we fall out here without being in a final state then we've
|
||||
// encountered something that the scanner can't match, which should
|
||||
// be impossible (the scanner matches all bytes _somehow_) but we'll
|
||||
// flag it anyway rather than just losing data from the end.
|
||||
if cs < formatfsm_first_final {
|
||||
return buf.String(), fmt.Errorf("extraneous characters beginning at offset %d", p)
|
||||
}
|
||||
|
||||
if highestArgIdx < len(a) {
|
||||
// Extraneous args are an error, to more easily detect mistakes
|
||||
firstBad := highestArgIdx+1
|
||||
if highestArgIdx == 0 {
|
||||
// Custom error message for this case
|
||||
return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; no verbs in format string")
|
||||
}
|
||||
return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; only %d used by format string", highestArgIdx)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
107
vendor/github.com/zclconf/go-cty/cty/function/stdlib/general.go
generated
vendored
Normal file
107
vendor/github.com/zclconf/go-cty/cty/function/stdlib/general.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var EqualFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowUnknown: true,
|
||||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowUnknown: true,
|
||||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].Equals(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var NotEqualFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowUnknown: true,
|
||||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowUnknown: true,
|
||||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].Equals(args[1]).Not(), nil
|
||||
},
|
||||
})
|
||||
|
||||
var CoalesceFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "vals",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowUnknown: true,
|
||||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
Type: func(args []cty.Value) (ret cty.Type, err error) {
|
||||
argTypes := make([]cty.Type, len(args))
|
||||
for i, val := range args {
|
||||
argTypes[i] = val.Type()
|
||||
}
|
||||
retType, _ := convert.UnifyUnsafe(argTypes)
|
||||
if retType == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("all arguments must have the same type")
|
||||
}
|
||||
return retType, nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
for _, argVal := range args {
|
||||
if !argVal.IsKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
if argVal.IsNull() {
|
||||
continue
|
||||
}
|
||||
|
||||
return convert.Convert(argVal, retType)
|
||||
}
|
||||
return cty.NilVal, fmt.Errorf("no non-null arguments")
|
||||
},
|
||||
})
|
||||
|
||||
// Equal determines whether the two given values are equal, returning a
|
||||
// bool value.
|
||||
func Equal(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return EqualFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// NotEqual is the opposite of Equal.
|
||||
func NotEqual(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return NotEqualFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Coalesce returns the first of the given arguments that is not null. If
|
||||
// all arguments are null, an error is produced.
|
||||
func Coalesce(vals ...cty.Value) (cty.Value, error) {
|
||||
return CoalesceFunc.Call(vals)
|
||||
}
|
77
vendor/github.com/zclconf/go-cty/cty/function/stdlib/json.go
generated
vendored
Normal file
77
vendor/github.com/zclconf/go-cty/cty/function/stdlib/json.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/json"
|
||||
)
|
||||
|
||||
var JSONEncodeFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "val",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
val := args[0]
|
||||
if !val.IsWhollyKnown() {
|
||||
// We can't serialize unknowns, so if the value is unknown or
|
||||
// contains any _nested_ unknowns then our result must be
|
||||
// unknown.
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
if val.IsNull() {
|
||||
return cty.StringVal("null"), nil
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(val, val.Type())
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
|
||||
return cty.StringVal(string(buf)), nil
|
||||
},
|
||||
})
|
||||
|
||||
var JSONDecodeFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
str := args[0]
|
||||
if !str.IsKnown() {
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
|
||||
buf := []byte(str.AsString())
|
||||
return json.ImpliedType(buf)
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
buf := []byte(args[0].AsString())
|
||||
return json.Unmarshal(buf, retType)
|
||||
},
|
||||
})
|
||||
|
||||
// JSONEncode returns a JSON serialization of the given value.
|
||||
func JSONEncode(val cty.Value) (cty.Value, error) {
|
||||
return JSONEncodeFunc.Call([]cty.Value{val})
|
||||
}
|
||||
|
||||
// JSONDecode parses the given JSON string and, if it is valid, returns the
|
||||
// value it represents.
|
||||
//
|
||||
// Note that applying JSONDecode to the result of JSONEncode may not produce
|
||||
// an identically-typed result, since JSON encoding is lossy for cty Types.
|
||||
// The resulting value will consist only of primitive types, object types, and
|
||||
// tuple types.
|
||||
func JSONDecode(str cty.Value) (cty.Value, error) {
|
||||
return JSONDecodeFunc.Call([]cty.Value{str})
|
||||
}
|
667
vendor/github.com/zclconf/go-cty/cty/function/stdlib/number.go
generated
vendored
Normal file
667
vendor/github.com/zclconf/go-cty/cty/function/stdlib/number.go
generated
vendored
Normal file
@ -0,0 +1,667 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
var AbsoluteFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return args[0].Absolute(), nil
|
||||
},
|
||||
})
|
||||
|
||||
var AddFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
// big.Float.Add can panic if the input values are opposing infinities,
|
||||
// so we must catch that here in order to remain within
|
||||
// the cty Function abstraction.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(big.ErrNaN); ok {
|
||||
ret = cty.NilVal
|
||||
err = fmt.Errorf("can't compute sum of opposing infinities")
|
||||
} else {
|
||||
// not a panic we recognize
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return args[0].Add(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var SubtractFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
// big.Float.Sub can panic if the input values are infinities,
|
||||
// so we must catch that here in order to remain within
|
||||
// the cty Function abstraction.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(big.ErrNaN); ok {
|
||||
ret = cty.NilVal
|
||||
err = fmt.Errorf("can't subtract infinity from itself")
|
||||
} else {
|
||||
// not a panic we recognize
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return args[0].Subtract(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var MultiplyFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
// big.Float.Mul can panic if the input values are both zero or both
|
||||
// infinity, so we must catch that here in order to remain within
|
||||
// the cty Function abstraction.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(big.ErrNaN); ok {
|
||||
ret = cty.NilVal
|
||||
err = fmt.Errorf("can't multiply zero by infinity")
|
||||
} else {
|
||||
// not a panic we recognize
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return args[0].Multiply(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var DivideFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
// big.Float.Quo can panic if the input values are both zero or both
|
||||
// infinity, so we must catch that here in order to remain within
|
||||
// the cty Function abstraction.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(big.ErrNaN); ok {
|
||||
ret = cty.NilVal
|
||||
err = fmt.Errorf("can't divide zero by zero or infinity by infinity")
|
||||
} else {
|
||||
// not a panic we recognize
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return args[0].Divide(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var ModuloFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
// big.Float.Mul can panic if the input values are both zero or both
|
||||
// infinity, so we must catch that here in order to remain within
|
||||
// the cty Function abstraction.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(big.ErrNaN); ok {
|
||||
ret = cty.NilVal
|
||||
err = fmt.Errorf("can't use modulo with zero and infinity")
|
||||
} else {
|
||||
// not a panic we recognize
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return args[0].Modulo(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var GreaterThanFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].GreaterThan(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var GreaterThanOrEqualToFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].GreaterThanOrEqualTo(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var LessThanFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].LessThan(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var LessThanOrEqualToFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].LessThanOrEqualTo(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var NegateFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
AllowMarked: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return args[0].Negate(), nil
|
||||
},
|
||||
})
|
||||
|
||||
var MinFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "numbers",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
if len(args) == 0 {
|
||||
return cty.NilVal, fmt.Errorf("must pass at least one number")
|
||||
}
|
||||
|
||||
min := cty.PositiveInfinity
|
||||
for _, num := range args {
|
||||
if num.LessThan(min).True() {
|
||||
min = num
|
||||
}
|
||||
}
|
||||
|
||||
return min, nil
|
||||
},
|
||||
})
|
||||
|
||||
var MaxFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "numbers",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
if len(args) == 0 {
|
||||
return cty.NilVal, fmt.Errorf("must pass at least one number")
|
||||
}
|
||||
|
||||
max := cty.NegativeInfinity
|
||||
for _, num := range args {
|
||||
if num.GreaterThan(max).True() {
|
||||
max = num
|
||||
}
|
||||
}
|
||||
|
||||
return max, nil
|
||||
},
|
||||
})
|
||||
|
||||
var IntFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
bf := args[0].AsBigFloat()
|
||||
if bf.IsInt() {
|
||||
return args[0], nil
|
||||
}
|
||||
bi, _ := bf.Int(nil)
|
||||
bf = (&big.Float{}).SetInt(bi)
|
||||
return cty.NumberVal(bf), nil
|
||||
},
|
||||
})
|
||||
|
||||
// CeilFunc is a function that returns the closest whole number greater
|
||||
// than or equal to the given value.
|
||||
var CeilFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
f := args[0].AsBigFloat()
|
||||
|
||||
if f.IsInf() {
|
||||
return cty.NumberVal(f), nil
|
||||
}
|
||||
|
||||
i, acc := f.Int(nil)
|
||||
switch acc {
|
||||
case big.Exact, big.Above:
|
||||
// Done.
|
||||
case big.Below:
|
||||
i.Add(i, big.NewInt(1))
|
||||
}
|
||||
|
||||
return cty.NumberVal(f.SetInt(i)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// FloorFunc is a function that returns the closest whole number lesser
|
||||
// than or equal to the given value.
|
||||
var FloorFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
f := args[0].AsBigFloat()
|
||||
|
||||
if f.IsInf() {
|
||||
return cty.NumberVal(f), nil
|
||||
}
|
||||
|
||||
i, acc := f.Int(nil)
|
||||
switch acc {
|
||||
case big.Exact, big.Below:
|
||||
// Done.
|
||||
case big.Above:
|
||||
i.Sub(i, big.NewInt(1))
|
||||
}
|
||||
|
||||
return cty.NumberVal(f.SetInt(i)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// LogFunc is a function that returns the logarithm of a given number in a given base.
|
||||
var LogFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
},
|
||||
{
|
||||
Name: "base",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
var num float64
|
||||
if err := gocty.FromCtyValue(args[0], &num); err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
var base float64
|
||||
if err := gocty.FromCtyValue(args[1], &base); err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
return cty.NumberFloatVal(math.Log(num) / math.Log(base)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// PowFunc is a function that returns the logarithm of a given number in a given base.
|
||||
var PowFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
},
|
||||
{
|
||||
Name: "power",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
var num float64
|
||||
if err := gocty.FromCtyValue(args[0], &num); err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
var power float64
|
||||
if err := gocty.FromCtyValue(args[1], &power); err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
return cty.NumberFloatVal(math.Pow(num, power)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// SignumFunc is a function that determines the sign of a number, returning a
|
||||
// number between -1 and 1 to represent the sign..
|
||||
var SignumFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "num",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
var num int
|
||||
if err := gocty.FromCtyValue(args[0], &num); err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
switch {
|
||||
case num < 0:
|
||||
return cty.NumberIntVal(-1), nil
|
||||
case num > 0:
|
||||
return cty.NumberIntVal(+1), nil
|
||||
default:
|
||||
return cty.NumberIntVal(0), nil
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// ParseIntFunc is a function that parses a string argument and returns an integer of the specified base.
|
||||
var ParseIntFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "number",
|
||||
Type: cty.DynamicPseudoType,
|
||||
},
|
||||
{
|
||||
Name: "base",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
if !args[0].Type().Equals(cty.String) {
|
||||
return cty.Number, function.NewArgErrorf(0, "first argument must be a string, not %s", args[0].Type().FriendlyName())
|
||||
}
|
||||
return cty.Number, nil
|
||||
},
|
||||
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
var numstr string
|
||||
var base int
|
||||
var err error
|
||||
|
||||
if err = gocty.FromCtyValue(args[0], &numstr); err != nil {
|
||||
return cty.UnknownVal(cty.String), function.NewArgError(0, err)
|
||||
}
|
||||
|
||||
if err = gocty.FromCtyValue(args[1], &base); err != nil {
|
||||
return cty.UnknownVal(cty.Number), function.NewArgError(1, err)
|
||||
}
|
||||
|
||||
if base < 2 || base > 62 {
|
||||
return cty.UnknownVal(cty.Number), function.NewArgErrorf(
|
||||
1,
|
||||
"base must be a whole number between 2 and 62 inclusive",
|
||||
)
|
||||
}
|
||||
|
||||
num, ok := (&big.Int{}).SetString(numstr, base)
|
||||
if !ok {
|
||||
return cty.UnknownVal(cty.Number), function.NewArgErrorf(
|
||||
0,
|
||||
"cannot parse %q as a base %d integer",
|
||||
numstr,
|
||||
base,
|
||||
)
|
||||
}
|
||||
|
||||
parsedNum := cty.NumberVal((&big.Float{}).SetInt(num))
|
||||
|
||||
return parsedNum, nil
|
||||
},
|
||||
})
|
||||
|
||||
// Absolute returns the magnitude of the given number, without its sign.
|
||||
// That is, it turns negative values into positive values.
|
||||
func Absolute(num cty.Value) (cty.Value, error) {
|
||||
return AbsoluteFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// Add returns the sum of the two given numbers.
|
||||
func Add(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return AddFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Subtract returns the difference between the two given numbers.
|
||||
func Subtract(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return SubtractFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Multiply returns the product of the two given numbers.
|
||||
func Multiply(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return MultiplyFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Divide returns a divided by b, where both a and b are numbers.
|
||||
func Divide(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return DivideFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Negate returns the given number multipled by -1.
|
||||
func Negate(num cty.Value) (cty.Value, error) {
|
||||
return NegateFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// LessThan returns true if a is less than b.
|
||||
func LessThan(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return LessThanFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// LessThanOrEqualTo returns true if a is less than b.
|
||||
func LessThanOrEqualTo(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return LessThanOrEqualToFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// GreaterThan returns true if a is less than b.
|
||||
func GreaterThan(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return GreaterThanFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// GreaterThanOrEqualTo returns true if a is less than b.
|
||||
func GreaterThanOrEqualTo(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return GreaterThanOrEqualToFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Modulo returns the remainder of a divided by b under integer division,
|
||||
// where both a and b are numbers.
|
||||
func Modulo(a cty.Value, b cty.Value) (cty.Value, error) {
|
||||
return ModuloFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// Min returns the minimum number from the given numbers.
|
||||
func Min(numbers ...cty.Value) (cty.Value, error) {
|
||||
return MinFunc.Call(numbers)
|
||||
}
|
||||
|
||||
// Max returns the maximum number from the given numbers.
|
||||
func Max(numbers ...cty.Value) (cty.Value, error) {
|
||||
return MaxFunc.Call(numbers)
|
||||
}
|
||||
|
||||
// Int removes the fractional component of the given number returning an
|
||||
// integer representing the whole number component, rounding towards zero.
|
||||
// For example, -1.5 becomes -1.
|
||||
//
|
||||
// If an infinity is passed to Int, an error is returned.
|
||||
func Int(num cty.Value) (cty.Value, error) {
|
||||
if num == cty.PositiveInfinity || num == cty.NegativeInfinity {
|
||||
return cty.NilVal, fmt.Errorf("can't truncate infinity to an integer")
|
||||
}
|
||||
return IntFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// Ceil returns the closest whole number greater than or equal to the given value.
|
||||
func Ceil(num cty.Value) (cty.Value, error) {
|
||||
return CeilFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// Floor returns the closest whole number lesser than or equal to the given value.
|
||||
func Floor(num cty.Value) (cty.Value, error) {
|
||||
return FloorFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// Log returns returns the logarithm of a given number in a given base.
|
||||
func Log(num, base cty.Value) (cty.Value, error) {
|
||||
return LogFunc.Call([]cty.Value{num, base})
|
||||
}
|
||||
|
||||
// Pow returns the logarithm of a given number in a given base.
|
||||
func Pow(num, power cty.Value) (cty.Value, error) {
|
||||
return PowFunc.Call([]cty.Value{num, power})
|
||||
}
|
||||
|
||||
// Signum determines the sign of a number, returning a number between -1 and
|
||||
// 1 to represent the sign.
|
||||
func Signum(num cty.Value) (cty.Value, error) {
|
||||
return SignumFunc.Call([]cty.Value{num})
|
||||
}
|
||||
|
||||
// ParseInt parses a string argument and returns an integer of the specified base.
|
||||
func ParseInt(num cty.Value, base cty.Value) (cty.Value, error) {
|
||||
return ParseIntFunc.Call([]cty.Value{num, base})
|
||||
}
|
233
vendor/github.com/zclconf/go-cty/cty/function/stdlib/regexp.go
generated
vendored
Normal file
233
vendor/github.com/zclconf/go-cty/cty/function/stdlib/regexp.go
generated
vendored
Normal file
@ -0,0 +1,233 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
resyntax "regexp/syntax"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var RegexFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "pattern",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "string",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
if !args[0].IsKnown() {
|
||||
// We can't predict our type without seeing our pattern
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
|
||||
retTy, err := regexPatternResultType(args[0].AsString())
|
||||
if err != nil {
|
||||
err = function.NewArgError(0, err)
|
||||
}
|
||||
return retTy, err
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
if retType == cty.DynamicPseudoType {
|
||||
return cty.DynamicVal, nil
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(args[0].AsString())
|
||||
if err != nil {
|
||||
// Should never happen, since we checked this in the Type function above.
|
||||
return cty.NilVal, function.NewArgErrorf(0, "error parsing pattern: %s", err)
|
||||
}
|
||||
str := args[1].AsString()
|
||||
|
||||
captureIdxs := re.FindStringSubmatchIndex(str)
|
||||
if captureIdxs == nil {
|
||||
return cty.NilVal, fmt.Errorf("pattern did not match any part of the given string")
|
||||
}
|
||||
|
||||
return regexPatternResult(re, str, captureIdxs, retType), nil
|
||||
},
|
||||
})
|
||||
|
||||
var RegexAllFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "pattern",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "string",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
if !args[0].IsKnown() {
|
||||
// We can't predict our type without seeing our pattern,
|
||||
// but we do know it'll always be a list of something.
|
||||
return cty.List(cty.DynamicPseudoType), nil
|
||||
}
|
||||
|
||||
retTy, err := regexPatternResultType(args[0].AsString())
|
||||
if err != nil {
|
||||
err = function.NewArgError(0, err)
|
||||
}
|
||||
return cty.List(retTy), err
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
ety := retType.ElementType()
|
||||
if ety == cty.DynamicPseudoType {
|
||||
return cty.DynamicVal, nil
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(args[0].AsString())
|
||||
if err != nil {
|
||||
// Should never happen, since we checked this in the Type function above.
|
||||
return cty.NilVal, function.NewArgErrorf(0, "error parsing pattern: %s", err)
|
||||
}
|
||||
str := args[1].AsString()
|
||||
|
||||
captureIdxsEach := re.FindAllStringSubmatchIndex(str, -1)
|
||||
if len(captureIdxsEach) == 0 {
|
||||
return cty.ListValEmpty(ety), nil
|
||||
}
|
||||
|
||||
elems := make([]cty.Value, len(captureIdxsEach))
|
||||
for i, captureIdxs := range captureIdxsEach {
|
||||
elems[i] = regexPatternResult(re, str, captureIdxs, ety)
|
||||
}
|
||||
return cty.ListVal(elems), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Regex is a function that extracts one or more substrings from a given
|
||||
// string by applying a regular expression pattern, describing the first
|
||||
// match.
|
||||
//
|
||||
// The return type depends on the composition of the capture groups (if any)
|
||||
// in the pattern:
|
||||
//
|
||||
// - If there are no capture groups at all, the result is a single string
|
||||
// representing the entire matched pattern.
|
||||
// - If all of the capture groups are named, the result is an object whose
|
||||
// keys are the named groups and whose values are their sub-matches, or
|
||||
// null if a particular sub-group was inside another group that didn't
|
||||
// match.
|
||||
// - If none of the capture groups are named, the result is a tuple whose
|
||||
// elements are the sub-groups in order and whose values are their
|
||||
// sub-matches, or null if a particular sub-group was inside another group
|
||||
// that didn't match.
|
||||
// - It is invalid to use both named and un-named capture groups together in
|
||||
// the same pattern.
|
||||
//
|
||||
// If the pattern doesn't match, this function returns an error. To test for
|
||||
// a match, call RegexAll and check if the length of the result is greater
|
||||
// than zero.
|
||||
func Regex(pattern, str cty.Value) (cty.Value, error) {
|
||||
return RegexFunc.Call([]cty.Value{pattern, str})
|
||||
}
|
||||
|
||||
// RegexAll is similar to Regex but it finds all of the non-overlapping matches
|
||||
// in the given string and returns a list of them.
|
||||
//
|
||||
// The result type is always a list, whose element type is deduced from the
|
||||
// pattern in the same way as the return type for Regex is decided.
|
||||
//
|
||||
// If the pattern doesn't match at all, this function returns an empty list.
|
||||
func RegexAll(pattern, str cty.Value) (cty.Value, error) {
|
||||
return RegexAllFunc.Call([]cty.Value{pattern, str})
|
||||
}
|
||||
|
||||
// regexPatternResultType parses the given regular expression pattern and
|
||||
// returns the structural type that would be returned to represent its
|
||||
// capture groups.
|
||||
//
|
||||
// Returns an error if parsing fails or if the pattern uses a mixture of
|
||||
// named and unnamed capture groups, which is not permitted.
|
||||
func regexPatternResultType(pattern string) (cty.Type, error) {
|
||||
re, rawErr := regexp.Compile(pattern)
|
||||
switch err := rawErr.(type) {
|
||||
case *resyntax.Error:
|
||||
return cty.NilType, fmt.Errorf("invalid regexp pattern: %s in %s", err.Code, err.Expr)
|
||||
case error:
|
||||
// Should never happen, since all regexp compile errors should
|
||||
// be resyntax.Error, but just in case...
|
||||
return cty.NilType, fmt.Errorf("error parsing pattern: %s", err)
|
||||
}
|
||||
|
||||
allNames := re.SubexpNames()[1:]
|
||||
var names []string
|
||||
unnamed := 0
|
||||
for _, name := range allNames {
|
||||
if name == "" {
|
||||
unnamed++
|
||||
} else {
|
||||
if names == nil {
|
||||
names = make([]string, 0, len(allNames))
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case unnamed == 0 && len(names) == 0:
|
||||
// If there are no capture groups at all then we'll return just a
|
||||
// single string for the whole match.
|
||||
return cty.String, nil
|
||||
case unnamed > 0 && len(names) > 0:
|
||||
return cty.NilType, fmt.Errorf("invalid regexp pattern: cannot mix both named and unnamed capture groups")
|
||||
case unnamed > 0:
|
||||
// For unnamed captures, we return a tuple of them all in order.
|
||||
etys := make([]cty.Type, unnamed)
|
||||
for i := range etys {
|
||||
etys[i] = cty.String
|
||||
}
|
||||
return cty.Tuple(etys), nil
|
||||
default:
|
||||
// For named captures, we return an object using the capture names
|
||||
// as keys.
|
||||
atys := make(map[string]cty.Type, len(names))
|
||||
for _, name := range names {
|
||||
atys[name] = cty.String
|
||||
}
|
||||
return cty.Object(atys), nil
|
||||
}
|
||||
}
|
||||
|
||||
func regexPatternResult(re *regexp.Regexp, str string, captureIdxs []int, retType cty.Type) cty.Value {
|
||||
switch {
|
||||
case retType == cty.String:
|
||||
start, end := captureIdxs[0], captureIdxs[1]
|
||||
return cty.StringVal(str[start:end])
|
||||
case retType.IsTupleType():
|
||||
captureIdxs = captureIdxs[2:] // index 0 is the whole pattern span, which we ignore by skipping one pair
|
||||
vals := make([]cty.Value, len(captureIdxs)/2)
|
||||
for i := range vals {
|
||||
start, end := captureIdxs[i*2], captureIdxs[i*2+1]
|
||||
if start < 0 || end < 0 {
|
||||
vals[i] = cty.NullVal(cty.String) // Did not match anything because containing group didn't match
|
||||
continue
|
||||
}
|
||||
vals[i] = cty.StringVal(str[start:end])
|
||||
}
|
||||
return cty.TupleVal(vals)
|
||||
case retType.IsObjectType():
|
||||
captureIdxs = captureIdxs[2:] // index 0 is the whole pattern span, which we ignore by skipping one pair
|
||||
vals := make(map[string]cty.Value, len(captureIdxs)/2)
|
||||
names := re.SubexpNames()[1:]
|
||||
for i, name := range names {
|
||||
start, end := captureIdxs[i*2], captureIdxs[i*2+1]
|
||||
if start < 0 || end < 0 {
|
||||
vals[name] = cty.NullVal(cty.String) // Did not match anything because containing group didn't match
|
||||
continue
|
||||
}
|
||||
vals[name] = cty.StringVal(str[start:end])
|
||||
}
|
||||
return cty.ObjectVal(vals)
|
||||
default:
|
||||
// Should never happen
|
||||
panic(fmt.Sprintf("invalid return type %#v", retType))
|
||||
}
|
||||
}
|
235
vendor/github.com/zclconf/go-cty/cty/function/stdlib/sequence.go
generated
vendored
Normal file
235
vendor/github.com/zclconf/go-cty/cty/function/stdlib/sequence.go
generated
vendored
Normal file
@ -0,0 +1,235 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var ConcatFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "seqs",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowMarked: true,
|
||||
},
|
||||
Type: func(args []cty.Value) (ret cty.Type, err error) {
|
||||
if len(args) == 0 {
|
||||
return cty.NilType, fmt.Errorf("at least one argument is required")
|
||||
}
|
||||
|
||||
if args[0].Type().IsListType() {
|
||||
// Possibly we're going to return a list, if all of our other
|
||||
// args are also lists and we can find a common element type.
|
||||
tys := make([]cty.Type, len(args))
|
||||
for i, val := range args {
|
||||
ty := val.Type()
|
||||
if !ty.IsListType() {
|
||||
tys = nil
|
||||
break
|
||||
}
|
||||
tys[i] = ty
|
||||
}
|
||||
|
||||
if tys != nil {
|
||||
commonType, _ := convert.UnifyUnsafe(tys)
|
||||
if commonType != cty.NilType {
|
||||
return commonType, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
etys := make([]cty.Type, 0, len(args))
|
||||
for i, val := range args {
|
||||
// Discard marks for nested values, as we only need to handle types
|
||||
// and lengths.
|
||||
val, _ := val.UnmarkDeep()
|
||||
|
||||
ety := val.Type()
|
||||
switch {
|
||||
case ety.IsTupleType():
|
||||
etys = append(etys, ety.TupleElementTypes()...)
|
||||
case ety.IsListType():
|
||||
if !val.IsKnown() {
|
||||
// We need to know the list to count its elements to
|
||||
// build our tuple type, so any concat of an unknown
|
||||
// list can't be typed yet.
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
|
||||
l := val.LengthInt()
|
||||
subEty := ety.ElementType()
|
||||
for j := 0; j < l; j++ {
|
||||
etys = append(etys, subEty)
|
||||
}
|
||||
default:
|
||||
return cty.NilType, function.NewArgErrorf(
|
||||
i, "all arguments must be lists or tuples; got %s",
|
||||
ety.FriendlyName(),
|
||||
)
|
||||
}
|
||||
}
|
||||
return cty.Tuple(etys), nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
switch {
|
||||
case retType.IsListType():
|
||||
// If retType is a list type then we know that all of the
|
||||
// given values will be lists and that they will either be of
|
||||
// retType or of something we can convert to retType.
|
||||
vals := make([]cty.Value, 0, len(args))
|
||||
var markses []cty.ValueMarks // remember any marked lists we find
|
||||
for i, list := range args {
|
||||
list, err = convert.Convert(list, retType)
|
||||
if err != nil {
|
||||
// Conversion might fail because we used UnifyUnsafe
|
||||
// to choose our return type.
|
||||
return cty.NilVal, function.NewArgError(i, err)
|
||||
}
|
||||
|
||||
list, listMarks := list.Unmark()
|
||||
if len(listMarks) > 0 {
|
||||
markses = append(markses, listMarks)
|
||||
}
|
||||
|
||||
it := list.ElementIterator()
|
||||
for it.Next() {
|
||||
_, v := it.Element()
|
||||
vals = append(vals, v)
|
||||
}
|
||||
}
|
||||
if len(vals) == 0 {
|
||||
return cty.ListValEmpty(retType.ElementType()).WithMarks(markses...), nil
|
||||
}
|
||||
|
||||
return cty.ListVal(vals).WithMarks(markses...), nil
|
||||
case retType.IsTupleType():
|
||||
// If retType is a tuple type then we could have a mixture of
|
||||
// lists and tuples but we know they all have known values
|
||||
// (because our params don't AllowUnknown) and we know that
|
||||
// concatenating them all together will produce a tuple of
|
||||
// retType because of the work we did in the Type function above.
|
||||
vals := make([]cty.Value, 0, len(args))
|
||||
var markses []cty.ValueMarks // remember any marked seqs we find
|
||||
|
||||
for _, seq := range args {
|
||||
seq, seqMarks := seq.Unmark()
|
||||
if len(seqMarks) > 0 {
|
||||
markses = append(markses, seqMarks)
|
||||
}
|
||||
|
||||
// Both lists and tuples support ElementIterator, so this is easy.
|
||||
it := seq.ElementIterator()
|
||||
for it.Next() {
|
||||
_, v := it.Element()
|
||||
vals = append(vals, v)
|
||||
}
|
||||
}
|
||||
|
||||
return cty.TupleVal(vals).WithMarks(markses...), nil
|
||||
default:
|
||||
// should never happen if Type is working correctly above
|
||||
panic("unsupported return type")
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
var RangeFunc = function.New(&function.Spec{
|
||||
VarParam: &function.Parameter{
|
||||
Name: "params",
|
||||
Type: cty.Number,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.Number)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
var start, end, step cty.Value
|
||||
switch len(args) {
|
||||
case 1:
|
||||
if args[0].LessThan(cty.Zero).True() {
|
||||
start, end, step = cty.Zero, args[0], cty.NumberIntVal(-1)
|
||||
} else {
|
||||
start, end, step = cty.Zero, args[0], cty.NumberIntVal(1)
|
||||
}
|
||||
case 2:
|
||||
if args[1].LessThan(args[0]).True() {
|
||||
start, end, step = args[0], args[1], cty.NumberIntVal(-1)
|
||||
} else {
|
||||
start, end, step = args[0], args[1], cty.NumberIntVal(1)
|
||||
}
|
||||
case 3:
|
||||
start, end, step = args[0], args[1], args[2]
|
||||
default:
|
||||
return cty.NilVal, fmt.Errorf("must have one, two, or three arguments")
|
||||
}
|
||||
|
||||
var vals []cty.Value
|
||||
|
||||
if step == cty.Zero {
|
||||
return cty.NilVal, function.NewArgErrorf(2, "step must not be zero")
|
||||
}
|
||||
down := step.LessThan(cty.Zero).True()
|
||||
|
||||
if down {
|
||||
if end.GreaterThan(start).True() {
|
||||
return cty.NilVal, function.NewArgErrorf(1, "end must be less than start when step is negative")
|
||||
}
|
||||
} else {
|
||||
if end.LessThan(start).True() {
|
||||
return cty.NilVal, function.NewArgErrorf(1, "end must be greater than start when step is positive")
|
||||
}
|
||||
}
|
||||
|
||||
num := start
|
||||
for {
|
||||
if down {
|
||||
if num.LessThanOrEqualTo(end).True() {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if num.GreaterThanOrEqualTo(end).True() {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(vals) >= 1024 {
|
||||
// Artificial limit to prevent bad arguments from consuming huge amounts of memory
|
||||
return cty.NilVal, fmt.Errorf("more than 1024 values were generated; either decrease the difference between start and end or use a smaller step")
|
||||
}
|
||||
vals = append(vals, num)
|
||||
num = num.Add(step)
|
||||
}
|
||||
if len(vals) == 0 {
|
||||
return cty.ListValEmpty(cty.Number), nil
|
||||
}
|
||||
return cty.ListVal(vals), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Concat takes one or more sequences (lists or tuples) and returns the single
|
||||
// sequence that results from concatenating them together in order.
|
||||
//
|
||||
// If all of the given sequences are lists of the same element type then the
|
||||
// result is a list of that type. Otherwise, the result is a of a tuple type
|
||||
// constructed from the given sequence types.
|
||||
func Concat(seqs ...cty.Value) (cty.Value, error) {
|
||||
return ConcatFunc.Call(seqs)
|
||||
}
|
||||
|
||||
// Range creates a list of numbers by starting from the given starting value,
|
||||
// then adding the given step value until the result is greater than or
|
||||
// equal to the given stopping value. Each intermediate result becomes an
|
||||
// element in the resulting list.
|
||||
//
|
||||
// When all three parameters are set, the order is (start, end, step). If
|
||||
// only two parameters are set, they are the start and end respectively and
|
||||
// step defaults to 1. If only one argument is set, it gives the end value
|
||||
// with start defaulting to 0 and step defaulting to 1.
|
||||
//
|
||||
// Because the resulting list must be fully buffered in memory, there is an
|
||||
// artificial cap of 1024 elements, after which this function will return
|
||||
// an error to avoid consuming unbounded amounts of memory. The Range function
|
||||
// is primarily intended for creating small lists of indices to iterate over,
|
||||
// so there should be no reason to generate huge lists with it.
|
||||
func Range(params ...cty.Value) (cty.Value, error) {
|
||||
return RangeFunc.Call(params)
|
||||
}
|
222
vendor/github.com/zclconf/go-cty/cty/function/stdlib/set.go
generated
vendored
Normal file
222
vendor/github.com/zclconf/go-cty/cty/function/stdlib/set.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var SetHasElementFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "set",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "elem",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return args[0].HasElement(args[1]), nil
|
||||
},
|
||||
})
|
||||
|
||||
var SetUnionFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "first_set",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "other_sets",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Union(s2)
|
||||
}, true),
|
||||
})
|
||||
|
||||
var SetIntersectionFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "first_set",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "other_sets",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Intersection(s2)
|
||||
}, false),
|
||||
})
|
||||
|
||||
var SetSubtractFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "a",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Subtract(s2)
|
||||
}, false),
|
||||
})
|
||||
|
||||
var SetSymmetricDifferenceFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "first_set",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "other_sets",
|
||||
Type: cty.Set(cty.DynamicPseudoType),
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.SymmetricDifference(s2)
|
||||
}, false),
|
||||
})
|
||||
|
||||
// SetHasElement determines whether the given set contains the given value as an
|
||||
// element.
|
||||
func SetHasElement(set cty.Value, elem cty.Value) (cty.Value, error) {
|
||||
return SetHasElementFunc.Call([]cty.Value{set, elem})
|
||||
}
|
||||
|
||||
// SetUnion returns a new set containing all of the elements from the given
|
||||
// sets, which must have element types that can all be converted to some
|
||||
// common type using the standard type unification rules. If conversion
|
||||
// is not possible, an error is returned.
|
||||
//
|
||||
// The union operation is performed after type conversion, which may result
|
||||
// in some previously-distinct values being conflated.
|
||||
//
|
||||
// At least one set must be provided.
|
||||
func SetUnion(sets ...cty.Value) (cty.Value, error) {
|
||||
return SetUnionFunc.Call(sets)
|
||||
}
|
||||
|
||||
// Intersection returns a new set containing the elements that exist
|
||||
// in all of the given sets, which must have element types that can all be
|
||||
// converted to some common type using the standard type unification rules.
|
||||
// If conversion is not possible, an error is returned.
|
||||
//
|
||||
// The intersection operation is performed after type conversion, which may
|
||||
// result in some previously-distinct values being conflated.
|
||||
//
|
||||
// At least one set must be provided.
|
||||
func SetIntersection(sets ...cty.Value) (cty.Value, error) {
|
||||
return SetIntersectionFunc.Call(sets)
|
||||
}
|
||||
|
||||
// SetSubtract returns a new set containing the elements from the
|
||||
// first set that are not present in the second set. The sets must have
|
||||
// element types that can both be converted to some common type using the
|
||||
// standard type unification rules. If conversion is not possible, an error
|
||||
// is returned.
|
||||
//
|
||||
// The subtract operation is performed after type conversion, which may
|
||||
// result in some previously-distinct values being conflated.
|
||||
func SetSubtract(a, b cty.Value) (cty.Value, error) {
|
||||
return SetSubtractFunc.Call([]cty.Value{a, b})
|
||||
}
|
||||
|
||||
// SetSymmetricDifference returns a new set containing elements that appear
|
||||
// in any of the given sets but not multiple. The sets must have
|
||||
// element types that can all be converted to some common type using the
|
||||
// standard type unification rules. If conversion is not possible, an error
|
||||
// is returned.
|
||||
//
|
||||
// The difference operation is performed after type conversion, which may
|
||||
// result in some previously-distinct values being conflated.
|
||||
func SetSymmetricDifference(sets ...cty.Value) (cty.Value, error) {
|
||||
return SetSymmetricDifferenceFunc.Call(sets)
|
||||
}
|
||||
|
||||
func setOperationReturnType(args []cty.Value) (ret cty.Type, err error) {
|
||||
var etys []cty.Type
|
||||
for _, arg := range args {
|
||||
ty := arg.Type().ElementType()
|
||||
|
||||
// Do not unify types for empty dynamic pseudo typed collections. These
|
||||
// will always convert to any other concrete type.
|
||||
if arg.IsKnown() && arg.LengthInt() == 0 && ty.Equals(cty.DynamicPseudoType) {
|
||||
continue
|
||||
}
|
||||
|
||||
etys = append(etys, ty)
|
||||
}
|
||||
|
||||
// If all element types were skipped (due to being empty dynamic collections),
|
||||
// the return type should also be a set of dynamic pseudo type.
|
||||
if len(etys) == 0 {
|
||||
return cty.Set(cty.DynamicPseudoType), nil
|
||||
}
|
||||
|
||||
newEty, _ := convert.UnifyUnsafe(etys)
|
||||
if newEty == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("given sets must all have compatible element types")
|
||||
}
|
||||
return cty.Set(newEty), nil
|
||||
}
|
||||
|
||||
func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet, allowUnknowns bool) function.ImplFunc {
|
||||
return func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
first := args[0]
|
||||
first, err = convert.Convert(first, retType)
|
||||
if err != nil {
|
||||
return cty.NilVal, function.NewArgError(0, err)
|
||||
}
|
||||
if !allowUnknowns && !first.IsWhollyKnown() {
|
||||
// This set function can produce a correct result only when all
|
||||
// elements are known, because eventually knowing the unknown
|
||||
// values may cause the result to have fewer known elements, or
|
||||
// might cause a result with no unknown elements at all to become
|
||||
// one with a different length.
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
set := first.AsValueSet()
|
||||
for i, arg := range args[1:] {
|
||||
arg, err := convert.Convert(arg, retType)
|
||||
if err != nil {
|
||||
return cty.NilVal, function.NewArgError(i+1, err)
|
||||
}
|
||||
if !allowUnknowns && !arg.IsWhollyKnown() {
|
||||
// (For the same reason as we did this check for "first" above.)
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
argSet := arg.AsValueSet()
|
||||
set = f(set, argSet)
|
||||
}
|
||||
return cty.SetValFromValueSet(set), nil
|
||||
}
|
||||
}
|
546
vendor/github.com/zclconf/go-cty/cty/function/stdlib/string.go
generated
vendored
Normal file
546
vendor/github.com/zclconf/go-cty/cty/function/stdlib/string.go
generated
vendored
Normal file
@ -0,0 +1,546 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/apparentlymart/go-textseg/v13/textseg"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
var UpperFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
in := args[0].AsString()
|
||||
out := strings.ToUpper(in)
|
||||
return cty.StringVal(out), nil
|
||||
},
|
||||
})
|
||||
|
||||
var LowerFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
in := args[0].AsString()
|
||||
out := strings.ToLower(in)
|
||||
return cty.StringVal(out), nil
|
||||
},
|
||||
})
|
||||
|
||||
var ReverseFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
in := []byte(args[0].AsString())
|
||||
out := make([]byte, len(in))
|
||||
pos := len(out)
|
||||
|
||||
inB := []byte(in)
|
||||
for i := 0; i < len(in); {
|
||||
d, _, _ := textseg.ScanGraphemeClusters(inB[i:], true)
|
||||
cluster := in[i : i+d]
|
||||
pos -= len(cluster)
|
||||
copy(out[pos:], cluster)
|
||||
i += d
|
||||
}
|
||||
|
||||
return cty.StringVal(string(out)), nil
|
||||
},
|
||||
})
|
||||
|
||||
var StrlenFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Number),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
in := args[0].AsString()
|
||||
l := 0
|
||||
|
||||
inB := []byte(in)
|
||||
for i := 0; i < len(in); {
|
||||
d, _, _ := textseg.ScanGraphemeClusters(inB[i:], true)
|
||||
l++
|
||||
i += d
|
||||
}
|
||||
|
||||
return cty.NumberIntVal(int64(l)), nil
|
||||
},
|
||||
})
|
||||
|
||||
var SubstrFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "offset",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
{
|
||||
Name: "length",
|
||||
Type: cty.Number,
|
||||
AllowDynamicType: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
in := []byte(args[0].AsString())
|
||||
var offset, length int
|
||||
|
||||
var err error
|
||||
err = gocty.FromCtyValue(args[1], &offset)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
err = gocty.FromCtyValue(args[2], &length)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
totalLenNum, err := Strlen(args[0])
|
||||
if err != nil {
|
||||
// should never happen
|
||||
panic("Stdlen returned an error")
|
||||
}
|
||||
|
||||
var totalLen int
|
||||
err = gocty.FromCtyValue(totalLenNum, &totalLen)
|
||||
if err != nil {
|
||||
// should never happen
|
||||
panic("Stdlen returned a non-int number")
|
||||
}
|
||||
|
||||
offset += totalLen
|
||||
} else if length == 0 {
|
||||
// Short circuit here, after error checks, because if a
|
||||
// string of length 0 has been requested it will always
|
||||
// be the empty string
|
||||
return cty.StringVal(""), nil
|
||||
}
|
||||
|
||||
sub := in
|
||||
pos := 0
|
||||
var i int
|
||||
|
||||
// First we'll seek forward to our offset
|
||||
if offset > 0 {
|
||||
for i = 0; i < len(sub); {
|
||||
d, _, _ := textseg.ScanGraphemeClusters(sub[i:], true)
|
||||
i += d
|
||||
pos++
|
||||
if pos == offset {
|
||||
break
|
||||
}
|
||||
if i >= len(in) {
|
||||
return cty.StringVal(""), nil
|
||||
}
|
||||
}
|
||||
|
||||
sub = sub[i:]
|
||||
}
|
||||
|
||||
if length < 0 {
|
||||
// Taking the remainder of the string is a fast path since
|
||||
// we can just return the rest of the buffer verbatim.
|
||||
return cty.StringVal(string(sub)), nil
|
||||
}
|
||||
|
||||
// Otherwise we need to start seeking forward again until we
|
||||
// reach the length we want.
|
||||
pos = 0
|
||||
for i = 0; i < len(sub); {
|
||||
d, _, _ := textseg.ScanGraphemeClusters(sub[i:], true)
|
||||
i += d
|
||||
pos++
|
||||
if pos == length {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
sub = sub[:i]
|
||||
|
||||
return cty.StringVal(string(sub)), nil
|
||||
},
|
||||
})
|
||||
|
||||
var JoinFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "separator",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "lists",
|
||||
Type: cty.List(cty.String),
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
sep := args[0].AsString()
|
||||
listVals := args[1:]
|
||||
if len(listVals) < 1 {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("at least one list is required")
|
||||
}
|
||||
|
||||
l := 0
|
||||
for _, list := range listVals {
|
||||
if !list.IsWhollyKnown() {
|
||||
return cty.UnknownVal(cty.String), nil
|
||||
}
|
||||
l += list.LengthInt()
|
||||
}
|
||||
|
||||
items := make([]string, 0, l)
|
||||
for ai, list := range listVals {
|
||||
ei := 0
|
||||
for it := list.ElementIterator(); it.Next(); {
|
||||
_, val := it.Element()
|
||||
if val.IsNull() {
|
||||
if len(listVals) > 1 {
|
||||
return cty.UnknownVal(cty.String), function.NewArgErrorf(ai+1, "element %d of list %d is null; cannot concatenate null values", ei, ai+1)
|
||||
}
|
||||
return cty.UnknownVal(cty.String), function.NewArgErrorf(ai+1, "element %d is null; cannot concatenate null values", ei)
|
||||
}
|
||||
items = append(items, val.AsString())
|
||||
ei++
|
||||
}
|
||||
}
|
||||
|
||||
return cty.StringVal(strings.Join(items, sep)), nil
|
||||
},
|
||||
})
|
||||
|
||||
var SortFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "list",
|
||||
Type: cty.List(cty.String),
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.String)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
listVal := args[0]
|
||||
|
||||
if !listVal.IsWhollyKnown() {
|
||||
// If some of the element values aren't known yet then we
|
||||
// can't yet predict the order of the result.
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
if listVal.LengthInt() == 0 { // Easy path
|
||||
return listVal, nil
|
||||
}
|
||||
|
||||
list := make([]string, 0, listVal.LengthInt())
|
||||
for it := listVal.ElementIterator(); it.Next(); {
|
||||
iv, v := it.Element()
|
||||
if v.IsNull() {
|
||||
return cty.UnknownVal(retType), fmt.Errorf("given list element %s is null; a null string cannot be sorted", iv.AsBigFloat().String())
|
||||
}
|
||||
list = append(list, v.AsString())
|
||||
}
|
||||
|
||||
sort.Strings(list)
|
||||
retVals := make([]cty.Value, len(list))
|
||||
for i, s := range list {
|
||||
retVals[i] = cty.StringVal(s)
|
||||
}
|
||||
return cty.ListVal(retVals), nil
|
||||
},
|
||||
})
|
||||
|
||||
var SplitFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "separator",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.String)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
sep := args[0].AsString()
|
||||
str := args[1].AsString()
|
||||
elems := strings.Split(str, sep)
|
||||
elemVals := make([]cty.Value, len(elems))
|
||||
for i, s := range elems {
|
||||
elemVals[i] = cty.StringVal(s)
|
||||
}
|
||||
if len(elemVals) == 0 {
|
||||
return cty.ListValEmpty(cty.String), nil
|
||||
}
|
||||
return cty.ListVal(elemVals), nil
|
||||
},
|
||||
})
|
||||
|
||||
// ChompFunc is a function that removes newline characters at the end of a
|
||||
// string.
|
||||
var ChompFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
newlines := regexp.MustCompile(`(?:\r\n?|\n)*\z`)
|
||||
return cty.StringVal(newlines.ReplaceAllString(args[0].AsString(), "")), nil
|
||||
},
|
||||
})
|
||||
|
||||
// IndentFunc is a function that adds a given number of spaces to the
|
||||
// beginnings of all but the first line in a given multi-line string.
|
||||
var IndentFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "spaces",
|
||||
Type: cty.Number,
|
||||
},
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
var spaces int
|
||||
if err := gocty.FromCtyValue(args[0], &spaces); err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
data := args[1].AsString()
|
||||
pad := strings.Repeat(" ", spaces)
|
||||
return cty.StringVal(strings.Replace(data, "\n", "\n"+pad, -1)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// TitleFunc is a function that converts the first letter of each word in the
|
||||
// given string to uppercase.
|
||||
var TitleFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return cty.StringVal(strings.Title(args[0].AsString())), nil
|
||||
},
|
||||
})
|
||||
|
||||
// TrimSpaceFunc is a function that removes any space characters from the start
|
||||
// and end of the given string.
|
||||
var TrimSpaceFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
return cty.StringVal(strings.TrimSpace(args[0].AsString())), nil
|
||||
},
|
||||
})
|
||||
|
||||
// TrimFunc is a function that removes the specified characters from the start
|
||||
// and end of the given string.
|
||||
var TrimFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "cutset",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
str := args[0].AsString()
|
||||
cutset := args[1].AsString()
|
||||
return cty.StringVal(strings.Trim(str, cutset)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// TrimPrefixFunc is a function that removes the specified characters from the
|
||||
// start the given string.
|
||||
var TrimPrefixFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "prefix",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
str := args[0].AsString()
|
||||
prefix := args[1].AsString()
|
||||
return cty.StringVal(strings.TrimPrefix(str, prefix)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// TrimSuffixFunc is a function that removes the specified characters from the
|
||||
// end of the given string.
|
||||
var TrimSuffixFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "suffix",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
str := args[0].AsString()
|
||||
cutset := args[1].AsString()
|
||||
return cty.StringVal(strings.TrimSuffix(str, cutset)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Upper is a Function that converts a given string to uppercase.
|
||||
func Upper(str cty.Value) (cty.Value, error) {
|
||||
return UpperFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Lower is a Function that converts a given string to lowercase.
|
||||
func Lower(str cty.Value) (cty.Value, error) {
|
||||
return LowerFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Reverse is a Function that reverses the order of the characters in the
|
||||
// given string.
|
||||
//
|
||||
// As usual, "character" for the sake of this function is a grapheme cluster,
|
||||
// so combining diacritics (for example) will be considered together as a
|
||||
// single character.
|
||||
func Reverse(str cty.Value) (cty.Value, error) {
|
||||
return ReverseFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Strlen is a Function that returns the length of the given string in
|
||||
// characters.
|
||||
//
|
||||
// As usual, "character" for the sake of this function is a grapheme cluster,
|
||||
// so combining diacritics (for example) will be considered together as a
|
||||
// single character.
|
||||
func Strlen(str cty.Value) (cty.Value, error) {
|
||||
return StrlenFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Substr is a Function that extracts a sequence of characters from another
|
||||
// string and creates a new string.
|
||||
//
|
||||
// As usual, "character" for the sake of this function is a grapheme cluster,
|
||||
// so combining diacritics (for example) will be considered together as a
|
||||
// single character.
|
||||
//
|
||||
// The "offset" index may be negative, in which case it is relative to the
|
||||
// end of the given string.
|
||||
//
|
||||
// The "length" may be -1, in which case the remainder of the string after
|
||||
// the given offset will be returned.
|
||||
func Substr(str cty.Value, offset cty.Value, length cty.Value) (cty.Value, error) {
|
||||
return SubstrFunc.Call([]cty.Value{str, offset, length})
|
||||
}
|
||||
|
||||
// Join concatenates together the string elements of one or more lists with a
|
||||
// given separator.
|
||||
func Join(sep cty.Value, lists ...cty.Value) (cty.Value, error) {
|
||||
args := make([]cty.Value, len(lists)+1)
|
||||
args[0] = sep
|
||||
copy(args[1:], lists)
|
||||
return JoinFunc.Call(args)
|
||||
}
|
||||
|
||||
// Sort re-orders the elements of a given list of strings so that they are
|
||||
// in ascending lexicographical order.
|
||||
func Sort(list cty.Value) (cty.Value, error) {
|
||||
return SortFunc.Call([]cty.Value{list})
|
||||
}
|
||||
|
||||
// Split divides a given string by a given separator, returning a list of
|
||||
// strings containing the characters between the separator sequences.
|
||||
func Split(sep, str cty.Value) (cty.Value, error) {
|
||||
return SplitFunc.Call([]cty.Value{sep, str})
|
||||
}
|
||||
|
||||
// Chomp removes newline characters at the end of a string.
|
||||
func Chomp(str cty.Value) (cty.Value, error) {
|
||||
return ChompFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Indent adds a given number of spaces to the beginnings of all but the first
|
||||
// line in a given multi-line string.
|
||||
func Indent(spaces, str cty.Value) (cty.Value, error) {
|
||||
return IndentFunc.Call([]cty.Value{spaces, str})
|
||||
}
|
||||
|
||||
// Title converts the first letter of each word in the given string to uppercase.
|
||||
func Title(str cty.Value) (cty.Value, error) {
|
||||
return TitleFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// TrimSpace removes any space characters from the start and end of the given string.
|
||||
func TrimSpace(str cty.Value) (cty.Value, error) {
|
||||
return TrimSpaceFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Trim removes the specified characters from the start and end of the given string.
|
||||
func Trim(str, cutset cty.Value) (cty.Value, error) {
|
||||
return TrimFunc.Call([]cty.Value{str, cutset})
|
||||
}
|
||||
|
||||
// TrimPrefix removes the specified prefix from the start of the given string.
|
||||
func TrimPrefix(str, prefix cty.Value) (cty.Value, error) {
|
||||
return TrimPrefixFunc.Call([]cty.Value{str, prefix})
|
||||
}
|
||||
|
||||
// TrimSuffix removes the specified suffix from the end of the given string.
|
||||
func TrimSuffix(str, suffix cty.Value) (cty.Value, error) {
|
||||
return TrimSuffixFunc.Call([]cty.Value{str, suffix})
|
||||
}
|
80
vendor/github.com/zclconf/go-cty/cty/function/stdlib/string_replace.go
generated
vendored
Normal file
80
vendor/github.com/zclconf/go-cty/cty/function/stdlib/string_replace.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// ReplaceFunc is a function that searches a given string for another given
|
||||
// substring, and replaces each occurence with a given replacement string.
|
||||
// The substr argument is a simple string.
|
||||
var ReplaceFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "substr",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "replace",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
str := args[0].AsString()
|
||||
substr := args[1].AsString()
|
||||
replace := args[2].AsString()
|
||||
|
||||
return cty.StringVal(strings.Replace(str, substr, replace, -1)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// RegexReplaceFunc is a function that searches a given string for another
|
||||
// given substring, and replaces each occurence with a given replacement
|
||||
// string. The substr argument must be a valid regular expression.
|
||||
var RegexReplaceFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "substr",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "replace",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
str := args[0].AsString()
|
||||
substr := args[1].AsString()
|
||||
replace := args[2].AsString()
|
||||
|
||||
re, err := regexp.Compile(substr)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
return cty.StringVal(re.ReplaceAllString(str, replace)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Replace searches a given string for another given substring,
|
||||
// and replaces all occurrences with a given replacement string.
|
||||
func Replace(str, substr, replace cty.Value) (cty.Value, error) {
|
||||
return ReplaceFunc.Call([]cty.Value{str, substr, replace})
|
||||
}
|
||||
|
||||
func RegexReplace(str, substr, replace cty.Value) (cty.Value, error) {
|
||||
return RegexReplaceFunc.Call([]cty.Value{str, substr, replace})
|
||||
}
|
31
vendor/github.com/zclconf/go-cty/cty/function/unpredictable.go
generated
vendored
Normal file
31
vendor/github.com/zclconf/go-cty/cty/function/unpredictable.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Unpredictable wraps a given function such that it retains the same arguments
|
||||
// and type checking behavior but will return an unknown value when called.
|
||||
//
|
||||
// It is recommended that most functions be "pure", which is to say that they
|
||||
// will always produce the same value given particular input. However,
|
||||
// sometimes it is necessary to offer functions whose behavior depends on
|
||||
// some external state, such as reading a file or determining the current time.
|
||||
// In such cases, an unpredictable wrapper might be used to stand in for
|
||||
// the function during some sort of prior "checking" phase in order to delay
|
||||
// the actual effect until later.
|
||||
//
|
||||
// While Unpredictable can support a function that isn't pure in its
|
||||
// implementation, it still expects a function to be pure in its type checking
|
||||
// behavior, except for the special case of returning cty.DynamicPseudoType
|
||||
// if it is not yet able to predict its return value based on current argument
|
||||
// information.
|
||||
func Unpredictable(f Function) Function {
|
||||
newSpec := *f.spec // shallow copy
|
||||
newSpec.Impl = unpredictableImpl
|
||||
return New(&newSpec)
|
||||
}
|
||||
|
||||
func unpredictableImpl(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
204
vendor/github.com/zclconf/go-cty/cty/gob.go
generated
vendored
Normal file
204
vendor/github.com/zclconf/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/zclconf/go-cty/cty/set"
|
||||
)
|
||||
|
||||
// GobEncode is an implementation of the gob.GobEncoder interface, which
|
||||
// allows Values to be included in structures encoded with encoding/gob.
|
||||
//
|
||||
// Currently it is not possible to represent values of capsule types in gob,
|
||||
// because the types themselves cannot be represented.
|
||||
func (val Value) GobEncode() ([]byte, error) {
|
||||
if val.IsMarked() {
|
||||
return nil, errors.New("value is marked")
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
enc := gob.NewEncoder(buf)
|
||||
|
||||
gv := gobValue{
|
||||
Version: 0,
|
||||
Ty: val.ty,
|
||||
V: val.v,
|
||||
}
|
||||
|
||||
err := enc.Encode(gv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error encoding cty.Value: %s", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// GobDecode is an implementation of the gob.GobDecoder interface, which
|
||||
// inverts the operation performed by GobEncode. See the documentation of
|
||||
// GobEncode for considerations when using cty.Value instances with gob.
|
||||
func (val *Value) GobDecode(buf []byte) error {
|
||||
r := bytes.NewReader(buf)
|
||||
dec := gob.NewDecoder(r)
|
||||
|
||||
var gv gobValue
|
||||
err := dec.Decode(&gv)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding cty.Value: %s", err)
|
||||
}
|
||||
if gv.Version != 0 {
|
||||
return fmt.Errorf("unsupported cty.Value encoding version %d; only 0 is supported", gv.Version)
|
||||
}
|
||||
|
||||
// Because big.Float.GobEncode is implemented with a pointer reciever,
|
||||
// gob encoding of an interface{} containing a *big.Float value does not
|
||||
// round-trip correctly, emerging instead as a non-pointer big.Float.
|
||||
// The rest of cty expects all number values to be represented by
|
||||
// *big.Float, so we'll fix that up here.
|
||||
gv.V = gobDecodeFixNumberPtr(gv.V, gv.Ty)
|
||||
|
||||
val.ty = gv.Ty
|
||||
val.v = gv.V
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GobEncode is an implementation of the gob.GobEncoder interface, which
|
||||
// allows Types to be included in structures encoded with encoding/gob.
|
||||
//
|
||||
// Currently it is not possible to represent capsule types in gob.
|
||||
func (t Type) GobEncode() ([]byte, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
enc := gob.NewEncoder(buf)
|
||||
|
||||
gt := gobType{
|
||||
Version: 0,
|
||||
Impl: t.typeImpl,
|
||||
}
|
||||
|
||||
err := enc.Encode(gt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error encoding cty.Type: %s", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// GobDecode is an implementatino of the gob.GobDecoder interface, which
|
||||
// reverses the encoding performed by GobEncode to allow types to be recovered
|
||||
// from gob buffers.
|
||||
func (t *Type) GobDecode(buf []byte) error {
|
||||
r := bytes.NewReader(buf)
|
||||
dec := gob.NewDecoder(r)
|
||||
|
||||
var gt gobType
|
||||
err := dec.Decode(>)
|
||||
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/zclconf/go-cty/cty/gocty/doc.go
generated
vendored
Normal file
7
vendor/github.com/zclconf/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/zclconf/go-cty/cty/gocty/helpers.go
generated
vendored
Normal file
43
vendor/github.com/zclconf/go-cty/cty/gocty/helpers.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
package gocty
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/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/zclconf/go-cty/cty/gocty/in.go
generated
vendored
Normal file
548
vendor/github.com/zclconf/go-cty/cty/gocty/in.go
generated
vendored
Normal file
@ -0,0 +1,548 @@
|
||||
package gocty
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/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/zclconf/go-cty/cty/gocty/out.go
generated
vendored
Normal file
686
vendor/github.com/zclconf/go-cty/cty/gocty/out.go
generated
vendored
Normal file
@ -0,0 +1,686 @@
|
||||
package gocty
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"github.com/zclconf/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/zclconf/go-cty/cty/gocty/type_implied.go
generated
vendored
Normal file
108
vendor/github.com/zclconf/go-cty/cty/gocty/type_implied.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
package gocty
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/zclconf/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/zclconf/go-cty/cty/helper.go
generated
vendored
Normal file
99
vendor/github.com/zclconf/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
|
||||
}
|
199
vendor/github.com/zclconf/go-cty/cty/json.go
generated
vendored
Normal file
199
vendor/github.com/zclconf/go-cty/cty/json.go
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
package cty
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// 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)
|
||||
if optionals := t.OptionalAttributes(); len(optionals) > 0 {
|
||||
buf.WriteByte(',')
|
||||
optionalNames := make([]string, 0, len(optionals))
|
||||
for k := range optionals {
|
||||
optionalNames = append(optionalNames, k)
|
||||
}
|
||||
sort.Strings(optionalNames)
|
||||
optionalsJSON, err := json.Marshal(optionalNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(optionalsJSON)
|
||||
}
|
||||
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
|
||||
}
|
||||
if dec.More() {
|
||||
var optionals []string
|
||||
err = dec.Decode(&optionals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = ObjectWithOptionalAttrs(atys, optionals)
|
||||
} else {
|
||||
*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/zclconf/go-cty/cty/json/doc.go
generated
vendored
Normal file
11
vendor/github.com/zclconf/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/zclconf/go-cty/cty/json/marshal.go
generated
vendored
Normal file
193
vendor/github.com/zclconf/go-cty/cty/json/marshal.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
|
||||
"github.com/zclconf/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 serialized as JSON")
|
||||
}
|
||||
|
||||
// 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/zclconf/go-cty/cty/json/simple.go
generated
vendored
Normal file
41
vendor/github.com/zclconf/go-cty/cty/json/simple.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"github.com/zclconf/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/zclconf/go-cty/cty/json/type.go
generated
vendored
Normal file
23
vendor/github.com/zclconf/go-cty/cty/json/type.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"github.com/zclconf/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/zclconf/go-cty/cty/json/type_implied.go
generated
vendored
Normal file
170
vendor/github.com/zclconf/go-cty/cty/json/type_implied.go
generated
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/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/zclconf/go-cty/cty/json/unmarshal.go
generated
vendored
Normal file
459
vendor/github.com/zclconf/go-cty/cty/json/unmarshal.go
generated
vendored
Normal file
@ -0,0 +1,459 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/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/zclconf/go-cty/cty/json/value.go
generated
vendored
Normal file
65
vendor/github.com/zclconf/go-cty/cty/json/value.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/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/zclconf/go-cty/cty/list_type.go
generated
vendored
Normal file
74
vendor/github.com/zclconf/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/zclconf/go-cty/cty/map_type.go
generated
vendored
Normal file
74
vendor/github.com/zclconf/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
|
||||
}
|
389
vendor/github.com/zclconf/go-cty/cty/marks.go
generated
vendored
Normal file
389
vendor/github.com/zclconf/go-cty/cty/marks.go
generated
vendored
Normal file
@ -0,0 +1,389 @@
|
||||
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.
|
||||
//
|
||||
// If any of the arguments are already ValueMarks values then they'll be merged
|
||||
// into the result, rather than used directly as individual marks.
|
||||
func NewValueMarks(marks ...interface{}) ValueMarks {
|
||||
if len(marks) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make(ValueMarks, len(marks))
|
||||
for _, v := range marks {
|
||||
if vm, ok := v.(ValueMarks); ok {
|
||||
// Constructing a new ValueMarks with an existing ValueMarks
|
||||
// implements a merge operation. (This can cause our result to
|
||||
// have a larger size than we expected, but that's okay.)
|
||||
for v := range vm {
|
||||
ret[v] = struct{}{}
|
||||
}
|
||||
continue
|
||||
}
|
||||
ret[v] = struct{}{}
|
||||
}
|
||||
if len(ret) == 0 {
|
||||
// If we were merging ValueMarks values together and they were all
|
||||
// empty then we'll avoid returning a zero-length map and return a
|
||||
// nil instead, as is conventional.
|
||||
return nil
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
// PathValueMarks is a structure that enables tracking marks
|
||||
// and the paths where they are located in one type
|
||||
type PathValueMarks struct {
|
||||
Path Path
|
||||
Marks ValueMarks
|
||||
}
|
||||
|
||||
func (p PathValueMarks) Equal(o PathValueMarks) bool {
|
||||
if !p.Path.Equals(o.Path) {
|
||||
return false
|
||||
}
|
||||
if !p.Marks.Equal(o.Marks) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
// unwrap the inner marked value, so we don't get multiple layers
|
||||
// of marking.
|
||||
newMarker.realV = mr.realV
|
||||
} 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,
|
||||
}
|
||||
}
|
||||
|
||||
type applyPathValueMarksTransformer struct {
|
||||
pvm []PathValueMarks
|
||||
}
|
||||
|
||||
func (t *applyPathValueMarksTransformer) Enter(p Path, v Value) (Value, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (t *applyPathValueMarksTransformer) Exit(p Path, v Value) (Value, error) {
|
||||
for _, path := range t.pvm {
|
||||
if p.Equals(path.Path) {
|
||||
return v.WithMarks(path.Marks), nil
|
||||
}
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// MarkWithPaths accepts a slice of PathValueMarks to apply
|
||||
// markers to particular paths and returns the marked
|
||||
// Value.
|
||||
func (val Value) MarkWithPaths(pvm []PathValueMarks) Value {
|
||||
ret, _ := TransformWithTransformer(val, &applyPathValueMarksTransformer{pvm})
|
||||
return ret
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
type unmarkTransformer struct {
|
||||
pvm []PathValueMarks
|
||||
}
|
||||
|
||||
func (t *unmarkTransformer) Enter(p Path, v Value) (Value, error) {
|
||||
unmarkedVal, marks := v.Unmark()
|
||||
if len(marks) > 0 {
|
||||
path := make(Path, len(p), len(p)+1)
|
||||
copy(path, p)
|
||||
t.pvm = append(t.pvm, PathValueMarks{path, marks})
|
||||
}
|
||||
return unmarkedVal, nil
|
||||
}
|
||||
|
||||
func (t *unmarkTransformer) Exit(p Path, v Value) (Value, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
t := unmarkTransformer{}
|
||||
ret, _ := TransformWithTransformer(val, &t)
|
||||
|
||||
marks := make(ValueMarks)
|
||||
for _, pvm := range t.pvm {
|
||||
for m, s := range pvm.Marks {
|
||||
marks[m] = s
|
||||
}
|
||||
}
|
||||
|
||||
return ret, marks
|
||||
}
|
||||
|
||||
// UnmarkDeepWithPaths is like UnmarkDeep, except it returns a slice
|
||||
// of PathValueMarks rather than a superset of all marks. This allows
|
||||
// a caller to know which marks are associated with which paths
|
||||
// in the Value.
|
||||
func (val Value) UnmarkDeepWithPaths() (Value, []PathValueMarks) {
|
||||
t := unmarkTransformer{}
|
||||
ret, _ := TransformWithTransformer(val, &t)
|
||||
return ret, t.pvm
|
||||
}
|
||||
|
||||
func (val Value) unmarkForce() Value {
|
||||
unw, _ := val.Unmark()
|
||||
return unw
|
||||
}
|
||||
|
||||
// WithMarks returns a new value that has the same type and underlying value
|
||||
// as the receiver and also has the marks from the given maps (representing
|
||||
// sets).
|
||||
func (val Value) WithMarks(marks ...ValueMarks) Value {
|
||||
if len(marks) == 0 {
|
||||
return val
|
||||
}
|
||||
ownMarks := val.Marks()
|
||||
markCount := len(ownMarks)
|
||||
for _, s := range marks {
|
||||
markCount += len(s)
|
||||
}
|
||||
if markCount == 0 {
|
||||
return val
|
||||
}
|
||||
newMarks := make(ValueMarks, markCount)
|
||||
for m := range ownMarks {
|
||||
newMarks[m] = struct{}{}
|
||||
}
|
||||
for _, s := range marks {
|
||||
for m := range s {
|
||||
newMarks[m] = struct{}{}
|
||||
}
|
||||
}
|
||||
v := val.v
|
||||
if mr, ok := v.(marker); ok {
|
||||
v = mr.realV
|
||||
}
|
||||
return Value{
|
||||
ty: val.ty,
|
||||
v: marker{
|
||||
realV: v,
|
||||
marks: newMarks,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WithSameMarks returns a new value that has the same type and underlying
|
||||
// value as the receiver and also has the marks from the given source values.
|
||||
//
|
||||
// Use this if you are implementing your own higher-level operations against
|
||||
// cty using the integration methods, to re-introduce the marks from the
|
||||
// source values of the operation.
|
||||
func (val Value) WithSameMarks(srcs ...Value) Value {
|
||||
if len(srcs) == 0 {
|
||||
return val
|
||||
}
|
||||
ownMarks := val.Marks()
|
||||
markCount := len(ownMarks)
|
||||
for _, sv := range srcs {
|
||||
if mr, ok := sv.v.(marker); ok {
|
||||
markCount += len(mr.marks)
|
||||
}
|
||||
}
|
||||
if markCount == 0 {
|
||||
return val
|
||||
}
|
||||
newMarks := make(ValueMarks, markCount)
|
||||
for m := range ownMarks {
|
||||
newMarks[m] = struct{}{}
|
||||
}
|
||||
for _, sv := range srcs {
|
||||
if mr, ok := sv.v.(marker); ok {
|
||||
for m := range mr.marks {
|
||||
newMarks[m] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
v := val.v
|
||||
if mr, ok := v.(marker); ok {
|
||||
v = mr.realV
|
||||
}
|
||||
return Value{
|
||||
ty: val.ty,
|
||||
v: marker{
|
||||
realV: v,
|
||||
marks: newMarks,
|
||||
},
|
||||
}
|
||||
}
|
14
vendor/github.com/zclconf/go-cty/cty/msgpack/doc.go
generated
vendored
Normal file
14
vendor/github.com/zclconf/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/zclconf/go-cty/cty/msgpack/dynamic.go
generated
vendored
Normal file
31
vendor/github.com/zclconf/go-cty/cty/msgpack/dynamic.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package msgpack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/vmihailenco/msgpack/v4"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
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/zclconf/go-cty/cty/msgpack/infinity.go
generated
vendored
Normal file
8
vendor/github.com/zclconf/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)
|
212
vendor/github.com/zclconf/go-cty/cty/msgpack/marshal.go
generated
vendored
Normal file
212
vendor/github.com/zclconf/go-cty/cty/msgpack/marshal.go
generated
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
package msgpack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/vmihailenco/msgpack/v4"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
)
|
||||
|
||||
// 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)
|
||||
enc.UseCompactEncoding(true)
|
||||
|
||||
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 serialized")
|
||||
}
|
||||
|
||||
// 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/zclconf/go-cty/cty/msgpack/type_implied.go
generated
vendored
Normal file
167
vendor/github.com/zclconf/go-cty/cty/msgpack/type_implied.go
generated
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
package msgpack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/vmihailenco/msgpack/v4"
|
||||
msgpackcodes "github.com/vmihailenco/msgpack/v4/codes"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// 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/zclconf/go-cty/cty/msgpack/unknown.go
generated
vendored
Normal file
16
vendor/github.com/zclconf/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/zclconf/go-cty/cty/msgpack/unmarshal.go
generated
vendored
Normal file
334
vendor/github.com/zclconf/go-cty/cty/msgpack/unmarshal.go
generated
vendored
Normal file
@ -0,0 +1,334 @@
|
||||
package msgpack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/vmihailenco/msgpack/v4"
|
||||
msgpackCodes "github.com/vmihailenco/msgpack/v4/codes"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// 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/zclconf/go-cty/cty/null.go
generated
vendored
Normal file
14
vendor/github.com/zclconf/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,
|
||||
}
|
||||
}
|
220
vendor/github.com/zclconf/go-cty/cty/object_type.go
generated
vendored
Normal file
220
vendor/github.com/zclconf/go-cty/cty/object_type.go
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
package cty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type typeObject struct {
|
||||
typeImplSigil
|
||||
AttrTypes map[string]Type
|
||||
AttrOptional map[string]struct{}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return ObjectWithOptionalAttrs(attrTypes, nil)
|
||||
}
|
||||
|
||||
// ObjectWithOptionalAttrs creates an object type where some of its attributes
|
||||
// are optional.
|
||||
//
|
||||
// This function is EXPERIMENTAL. The behavior of the function or of any other
|
||||
// functions working either directly or indirectly with a type created by
|
||||
// this function is not currently considered as a compatibility constraint, and
|
||||
// is subject to change even in minor-version releases of this module. Other
|
||||
// modules that work with cty types and values may or may not support object
|
||||
// types with optional attributes; if they do not, their behavior when
|
||||
// receiving one may be non-ideal.
|
||||
//
|
||||
// Optional attributes are significant only when an object type is being used
|
||||
// as a target type for conversion in the "convert" package. A value of an
|
||||
// object type always has a value for each of the attributes in the attribute
|
||||
// types table, with optional values replaced with null during conversion.
|
||||
//
|
||||
// All keys in the optional slice must also exist in the attrTypes map. If not,
|
||||
// this function will panic.
|
||||
//
|
||||
// After a map or array is passed to this function the caller must no longer
|
||||
// access it, since ownership is transferred to this library.
|
||||
func ObjectWithOptionalAttrs(attrTypes map[string]Type, optional []string) Type {
|
||||
attrTypesNorm := make(map[string]Type, len(attrTypes))
|
||||
for k, v := range attrTypes {
|
||||
attrTypesNorm[NormalizeString(k)] = v
|
||||
}
|
||||
|
||||
var optionalSet map[string]struct{}
|
||||
if len(optional) > 0 {
|
||||
optionalSet = make(map[string]struct{}, len(optional))
|
||||
for _, k := range optional {
|
||||
k = NormalizeString(k)
|
||||
if _, exists := attrTypesNorm[k]; !exists {
|
||||
panic(fmt.Sprintf("optional contains undeclared attribute %q", k))
|
||||
}
|
||||
optionalSet[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return Type{
|
||||
typeObject{
|
||||
AttrTypes: attrTypesNorm,
|
||||
AttrOptional: optionalSet,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
_, opt := t.AttrOptional[attr]
|
||||
_, oopt := ot.AttrOptional[attr]
|
||||
if opt != oopt {
|
||||
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"
|
||||
}
|
||||
if len(t.AttrOptional) > 0 {
|
||||
var opt []string
|
||||
for k := range t.AttrOptional {
|
||||
opt = append(opt, k)
|
||||
}
|
||||
sort.Strings(opt)
|
||||
return fmt.Sprintf("cty.ObjectWithOptionalAttrs(%#v, %#v)", t.AttrTypes, opt)
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
// OptionalAttributes returns a map representing the set of attributes
|
||||
// that are optional. 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.
|
||||
func (t Type) OptionalAttributes() map[string]struct{} {
|
||||
if ot, ok := t.typeImpl.(typeObject); ok {
|
||||
return ot.AttrOptional
|
||||
}
|
||||
panic("OptionalAttributes on non-object Type")
|
||||
}
|
||||
|
||||
// AttributeOptional returns true if the attribute of the given name is
|
||||
// optional.
|
||||
//
|
||||
// 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) AttributeOptional(name string) bool {
|
||||
name = NormalizeString(name)
|
||||
if ot, ok := t.typeImpl.(typeObject); ok {
|
||||
if _, hasAttr := ot.AttrTypes[name]; !hasAttr {
|
||||
panic("no such attribute")
|
||||
}
|
||||
_, exists := ot.AttrOptional[name]
|
||||
return exists
|
||||
}
|
||||
panic("AttributeDefaultValue on non-object Type")
|
||||
}
|
270
vendor/github.com/zclconf/go-cty/cty/path.go
generated
vendored
Normal file
270
vendor/github.com/zclconf/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)
|
||||
}
|
204
vendor/github.com/zclconf/go-cty/cty/path_set.go
generated
vendored
Normal file
204
vendor/github.com/zclconf/go-cty/cty/path_set.go
generated
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
package cty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/crc64"
|
||||
|
||||
"github.com/zclconf/go-cty/cty/set"
|
||||
)
|
||||
|
||||
// PathSet represents a set of Path objects. This can be used, for example,
|
||||
// to talk about a subset of paths within a value that meet some criteria,
|
||||
// without directly modifying the values at those paths.
|
||||
type PathSet struct {
|
||||
set set.Set
|
||||
}
|
||||
|
||||
// NewPathSet creates and returns a PathSet, with initial contents optionally
|
||||
// set by the given arguments.
|
||||
func NewPathSet(paths ...Path) PathSet {
|
||||
ret := PathSet{
|
||||
set: set.NewSet(pathSetRules{}),
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
ret.Add(path)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Add inserts a single given path into the set.
|
||||
//
|
||||
// Paths are immutable after construction by convention. It is particularly
|
||||
// important not to mutate a path after it has been placed into a PathSet.
|
||||
// If a Path is mutated while in a set, behavior is undefined.
|
||||
func (s PathSet) Add(path Path) {
|
||||
s.set.Add(path)
|
||||
}
|
||||
|
||||
// AddAllSteps is like Add but it also adds all of the steps leading to
|
||||
// the given path.
|
||||
//
|
||||
// For example, if given a path representing "foo.bar", it will add both
|
||||
// "foo" and "bar".
|
||||
func (s PathSet) AddAllSteps(path Path) {
|
||||
for i := 1; i <= len(path); i++ {
|
||||
s.Add(path[:i])
|
||||
}
|
||||
}
|
||||
|
||||
// Has returns true if the given path is in the receiving set.
|
||||
func (s PathSet) Has(path Path) bool {
|
||||
return s.set.Has(path)
|
||||
}
|
||||
|
||||
// List makes and returns a slice of all of the paths in the receiving set,
|
||||
// in an undefined but consistent order.
|
||||
func (s PathSet) List() []Path {
|
||||
if s.Empty() {
|
||||
return nil
|
||||
}
|
||||
ret := make([]Path, 0, s.set.Length())
|
||||
for it := s.set.Iterator(); it.Next(); {
|
||||
ret = append(ret, it.Value().(Path))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Remove modifies the receving set to no longer include the given path.
|
||||
// If the given path was already absent, this is a no-op.
|
||||
func (s PathSet) Remove(path Path) {
|
||||
s.set.Remove(path)
|
||||
}
|
||||
|
||||
// Empty returns true if the length of the receiving set is zero.
|
||||
func (s PathSet) Empty() bool {
|
||||
return s.set.Length() == 0
|
||||
}
|
||||
|
||||
// Union returns a new set whose contents are the union of the receiver and
|
||||
// the given other set.
|
||||
func (s PathSet) Union(other PathSet) PathSet {
|
||||
return PathSet{
|
||||
set: s.set.Union(other.set),
|
||||
}
|
||||
}
|
||||
|
||||
// Intersection returns a new set whose contents are the intersection of the
|
||||
// receiver and the given other set.
|
||||
func (s PathSet) Intersection(other PathSet) PathSet {
|
||||
return PathSet{
|
||||
set: s.set.Intersection(other.set),
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract returns a new set whose contents are those from the receiver with
|
||||
// any elements of the other given set subtracted.
|
||||
func (s PathSet) Subtract(other PathSet) PathSet {
|
||||
return PathSet{
|
||||
set: s.set.Subtract(other.set),
|
||||
}
|
||||
}
|
||||
|
||||
// SymmetricDifference returns a new set whose contents are the symmetric
|
||||
// difference of the receiver and the given other set.
|
||||
func (s PathSet) SymmetricDifference(other PathSet) PathSet {
|
||||
return PathSet{
|
||||
set: s.set.SymmetricDifference(other.set),
|
||||
}
|
||||
}
|
||||
|
||||
// Equal returns true if and only if both the receiver and the given other
|
||||
// set contain exactly the same paths.
|
||||
func (s PathSet) Equal(other PathSet) bool {
|
||||
if s.set.Length() != other.set.Length() {
|
||||
return false
|
||||
}
|
||||
// Now we know the lengths are the same we only need to test in one
|
||||
// direction whether everything in one is in the other.
|
||||
for it := s.set.Iterator(); it.Next(); {
|
||||
if !other.set.Has(it.Value()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var crc64Table = crc64.MakeTable(crc64.ISO)
|
||||
|
||||
var indexStepPlaceholder = []byte("#")
|
||||
|
||||
// pathSetRules is an implementation of set.Rules from the set package,
|
||||
// used internally within PathSet.
|
||||
type pathSetRules struct {
|
||||
}
|
||||
|
||||
func (r pathSetRules) Hash(v interface{}) int {
|
||||
path := v.(Path)
|
||||
hash := crc64.New(crc64Table)
|
||||
|
||||
for _, rawStep := range path {
|
||||
switch step := rawStep.(type) {
|
||||
case GetAttrStep:
|
||||
// (this creates some garbage converting the string name to a
|
||||
// []byte, but that's okay since cty is not designed to be
|
||||
// used in tight loops under memory pressure.)
|
||||
hash.Write([]byte(step.Name))
|
||||
default:
|
||||
// For any other step type we just append a predefined value,
|
||||
// which means that e.g. all indexes into a given collection will
|
||||
// hash to the same value but we assume that collections are
|
||||
// small and thus this won't hurt too much.
|
||||
hash.Write(indexStepPlaceholder)
|
||||
}
|
||||
}
|
||||
|
||||
// We discard half of the hash on 32-bit platforms; collisions just make
|
||||
// our lookups take marginally longer, so not a big deal.
|
||||
return int(hash.Sum64())
|
||||
}
|
||||
|
||||
func (r pathSetRules) Equivalent(a, b interface{}) bool {
|
||||
aPath := a.(Path)
|
||||
bPath := b.(Path)
|
||||
|
||||
if len(aPath) != len(bPath) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range aPath {
|
||||
switch aStep := aPath[i].(type) {
|
||||
case GetAttrStep:
|
||||
bStep, ok := bPath[i].(GetAttrStep)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if aStep.Name != bStep.Name {
|
||||
return false
|
||||
}
|
||||
case IndexStep:
|
||||
bStep, ok := bPath[i].(IndexStep)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
eq := aStep.Key.Equals(bStep.Key)
|
||||
if !eq.IsKnown() || eq.False() {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
// Should never happen, since we document PathStep as a closed type.
|
||||
panic(fmt.Errorf("unsupported step type %T", aStep))
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// SameRules is true if both Rules instances are pathSetRules structs.
|
||||
func (r pathSetRules) SameRules(other set.Rules) bool {
|
||||
_, ok := other.(pathSetRules)
|
||||
return ok
|
||||
}
|
167
vendor/github.com/zclconf/go-cty/cty/primitive_type.go
generated
vendored
Normal file
167
vendor/github.com/zclconf/go-cty/cty/primitive_type.go
generated
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// rawNumberEqual is our cty-specific definition of whether two big floats
|
||||
// underlying cty.Number are "equal" for the purposes of the Value.Equals and
|
||||
// Value.RawEquals methods.
|
||||
//
|
||||
// The built-in equality for big.Float is a direct comparison of the mantissa
|
||||
// bits and the exponent, but that's too precise a check for cty because we
|
||||
// routinely send numbers through decimal approximations and back and so
|
||||
// we only promise to accurately represent the subset of binary floating point
|
||||
// numbers that can be derived from a decimal string representation.
|
||||
//
|
||||
// In respect of the fact that cty only tries to preserve numbers that can
|
||||
// reasonably be written in JSON documents, we use the string representation of
|
||||
// a decimal approximation of the number as our comparison, relying on the
|
||||
// big.Float type's heuristic for discarding extraneous mantissa bits that seem
|
||||
// likely to only be there as a result of an earlier decimal-to-binary
|
||||
// approximation during parsing, e.g. in ParseNumberVal.
|
||||
func rawNumberEqual(a, b *big.Float) bool {
|
||||
switch {
|
||||
case (a == nil) != (b == nil):
|
||||
return false
|
||||
case a == nil: // b == nil too then, due to previous case
|
||||
return true
|
||||
default:
|
||||
// This format and precision matches that used by cty/json.Marshal,
|
||||
// and thus achieves our definition of "two numbers are equal if
|
||||
// we'd use the same JSON serialization for both of them".
|
||||
const format = 'f'
|
||||
const prec = -1
|
||||
aStr := a.Text(format, prec)
|
||||
bStr := b.Text(format, prec)
|
||||
|
||||
// The one exception to our rule about equality-by-stringification is
|
||||
// negative zero, because we want -0 to always be equal to +0.
|
||||
const posZero = "0"
|
||||
const negZero = "-0"
|
||||
if aStr == negZero {
|
||||
aStr = posZero
|
||||
}
|
||||
if bStr == negZero {
|
||||
bStr = posZero
|
||||
}
|
||||
return aStr == bStr
|
||||
}
|
||||
}
|
||||
|
||||
// Number is the numeric type. Number values are arbitrary-precision
|
||||
// decimal numbers, which can then be converted into Go's various numeric
|
||||
// types only if they are in the appropriate range.
|
||||
var Number Type
|
||||
|
||||
// String is the string type. String values are sequences of unicode codepoints
|
||||
// encoded internally as UTF-8.
|
||||
var String Type
|
||||
|
||||
// Bool is the boolean type. The two values of this type are True and False.
|
||||
var Bool Type
|
||||
|
||||
// True is the truthy value of type Bool
|
||||
var True Value
|
||||
|
||||
// False is the falsey value of type Bool
|
||||
var False Value
|
||||
|
||||
// Zero is a number value representing exactly zero.
|
||||
var Zero Value
|
||||
|
||||
// PositiveInfinity is a Number value representing positive infinity
|
||||
var PositiveInfinity Value
|
||||
|
||||
// NegativeInfinity is a Number value representing negative infinity
|
||||
var NegativeInfinity Value
|
||||
|
||||
func init() {
|
||||
Number = Type{
|
||||
primitiveType{Kind: primitiveTypeNumber},
|
||||
}
|
||||
String = Type{
|
||||
primitiveType{Kind: primitiveTypeString},
|
||||
}
|
||||
Bool = Type{
|
||||
primitiveType{Kind: primitiveTypeBool},
|
||||
}
|
||||
True = Value{
|
||||
ty: Bool,
|
||||
v: true,
|
||||
}
|
||||
False = Value{
|
||||
ty: Bool,
|
||||
v: false,
|
||||
}
|
||||
Zero = Value{
|
||||
ty: Number,
|
||||
v: big.NewFloat(0),
|
||||
}
|
||||
PositiveInfinity = Value{
|
||||
ty: Number,
|
||||
v: (&big.Float{}).SetInf(false),
|
||||
}
|
||||
NegativeInfinity = Value{
|
||||
ty: Number,
|
||||
v: (&big.Float{}).SetInf(true),
|
||||
}
|
||||
}
|
||||
|
||||
// IsPrimitiveType returns true if and only if the reciever is a primitive
|
||||
// type, which means it's either number, string, or bool. Any two primitive
|
||||
// types can be safely compared for equality using the standard == operator
|
||||
// without panic, which is not a guarantee that holds for all types. Primitive
|
||||
// types can therefore also be used in switch statements.
|
||||
func (t Type) IsPrimitiveType() bool {
|
||||
_, ok := t.typeImpl.(primitiveType)
|
||||
return ok
|
||||
}
|
76
vendor/github.com/zclconf/go-cty/cty/set/gob.go
generated
vendored
Normal file
76
vendor/github.com/zclconf/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/zclconf/go-cty/cty/set/iterator.go
generated
vendored
Normal file
15
vendor/github.com/zclconf/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/zclconf/go-cty/cty/set/ops.go
generated
vendored
Normal file
210
vendor/github.com/zclconf/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
|
||||
}
|
47
vendor/github.com/zclconf/go-cty/cty/set/rules.go
generated
vendored
Normal file
47
vendor/github.com/zclconf/go-cty/cty/set/rules.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
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
|
||||
|
||||
// SameRules returns true if the instance is equivalent to another Rules
|
||||
// instance.
|
||||
SameRules(Rules) bool
|
||||
}
|
||||
|
||||
// OrderedRules is an extension of Rules that can apply a partial order to
|
||||
// element values. When a set's Rules implements OrderedRules an iterator
|
||||
// over the set will return items in the order described by the rules.
|
||||
//
|
||||
// If the given order is not a total order (that is, some pairs of non-equivalent
|
||||
// elements do not have a defined order) then the resulting iteration order
|
||||
// is undefined but consistent for a particular version of cty. The exact
|
||||
// order in that case is not part of the contract and is subject to change
|
||||
// between versions.
|
||||
type OrderedRules interface {
|
||||
Rules
|
||||
|
||||
// Less returns true if and only if the first argument should sort before
|
||||
// the second argument. If the second argument should sort before the first
|
||||
// or if there is no defined order for the values, return false.
|
||||
Less(interface{}, interface{}) bool
|
||||
}
|
62
vendor/github.com/zclconf/go-cty/cty/set/set.go
generated
vendored
Normal file
62
vendor/github.com/zclconf/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.SameRules(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.SameRules(rules)
|
||||
}
|
||||
|
||||
// Rules returns the receiving set's rules instance.
|
||||
func (s Set) Rules() Rules {
|
||||
return s.rules
|
||||
}
|
132
vendor/github.com/zclconf/go-cty/cty/set_helper.go
generated
vendored
Normal file
132
vendor/github.com/zclconf/go-cty/cty/set_helper.go
generated
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
package cty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty/set"
|
||||
)
|
||||
|
||||
// ValueSet is to cty.Set what []cty.Value is to cty.List and
|
||||
// map[string]cty.Value is to cty.Map. It's provided to allow callers a
|
||||
// convenient interface for manipulating sets before wrapping them in cty.Set
|
||||
// values using cty.SetValFromValueSet.
|
||||
//
|
||||
// Unlike value slices and value maps, ValueSet instances have a single
|
||||
// homogenous element type because that is a requirement of the underlying
|
||||
// set implementation, which uses the element type to select a suitable
|
||||
// hashing function.
|
||||
//
|
||||
// Set mutations are not concurrency-safe.
|
||||
type ValueSet struct {
|
||||
// ValueSet is just a thin wrapper around a set.Set with our value-oriented
|
||||
// "rules" applied. We do this so that the caller can work in terms of
|
||||
// cty.Value objects even though the set internals use the raw values.
|
||||
s set.Set
|
||||
}
|
||||
|
||||
// NewValueSet creates and returns a new ValueSet with the given element type.
|
||||
func NewValueSet(ety Type) ValueSet {
|
||||
return newValueSet(set.NewSet(setRules{Type: ety}))
|
||||
}
|
||||
|
||||
func newValueSet(s set.Set) ValueSet {
|
||||
return ValueSet{
|
||||
s: s,
|
||||
}
|
||||
}
|
||||
|
||||
// ElementType returns the element type for the receiving ValueSet.
|
||||
func (s ValueSet) ElementType() Type {
|
||||
return s.s.Rules().(setRules).Type
|
||||
}
|
||||
|
||||
// Add inserts the given value into the receiving set.
|
||||
func (s ValueSet) Add(v Value) {
|
||||
s.requireElementType(v)
|
||||
s.s.Add(v.v)
|
||||
}
|
||||
|
||||
// Remove deletes the given value from the receiving set, if indeed it was
|
||||
// there in the first place. If the value is not present, this is a no-op.
|
||||
func (s ValueSet) Remove(v Value) {
|
||||
s.requireElementType(v)
|
||||
s.s.Remove(v.v)
|
||||
}
|
||||
|
||||
// Has returns true if the given value is in the receiving set, or false if
|
||||
// it is not.
|
||||
func (s ValueSet) Has(v Value) bool {
|
||||
s.requireElementType(v)
|
||||
return s.s.Has(v.v)
|
||||
}
|
||||
|
||||
// Copy performs a shallow copy of the receiving set, returning a new set
|
||||
// with the same rules and elements.
|
||||
func (s ValueSet) Copy() ValueSet {
|
||||
return newValueSet(s.s.Copy())
|
||||
}
|
||||
|
||||
// Length returns the number of values in the set.
|
||||
func (s ValueSet) Length() int {
|
||||
return s.s.Length()
|
||||
}
|
||||
|
||||
// Values returns a slice of all of the values in the set in no particular
|
||||
// order.
|
||||
func (s ValueSet) Values() []Value {
|
||||
l := s.s.Length()
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make([]Value, 0, l)
|
||||
ety := s.ElementType()
|
||||
for it := s.s.Iterator(); it.Next(); {
|
||||
ret = append(ret, Value{
|
||||
ty: ety,
|
||||
v: it.Value(),
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Union returns a new set that contains all of the members of both the
|
||||
// receiving set and the given set. Both sets must have the same element type,
|
||||
// or else this function will panic.
|
||||
func (s ValueSet) Union(other ValueSet) ValueSet {
|
||||
return newValueSet(s.s.Union(other.s))
|
||||
}
|
||||
|
||||
// Intersection returns a new set that contains the values that both the
|
||||
// receiver and given sets have in common. Both sets must have the same element
|
||||
// type, or else this function will panic.
|
||||
func (s ValueSet) Intersection(other ValueSet) ValueSet {
|
||||
return newValueSet(s.s.Intersection(other.s))
|
||||
}
|
||||
|
||||
// Subtract returns a new set that contains all of the values from the receiver
|
||||
// that are not also in the given set. Both sets must have the same element
|
||||
// type, or else this function will panic.
|
||||
func (s ValueSet) Subtract(other ValueSet) ValueSet {
|
||||
return newValueSet(s.s.Subtract(other.s))
|
||||
}
|
||||
|
||||
// SymmetricDifference returns a new set that contains all of the values from
|
||||
// both the receiver and given sets, except those that both sets have in
|
||||
// common. Both sets must have the same element type, or else this function
|
||||
// will panic.
|
||||
func (s ValueSet) SymmetricDifference(other ValueSet) ValueSet {
|
||||
return newValueSet(s.s.SymmetricDifference(other.s))
|
||||
}
|
||||
|
||||
// requireElementType panics if the given value is not of the set's element type.
|
||||
//
|
||||
// It also panics if the given value is marked, because marked values cannot
|
||||
// be stored in sets.
|
||||
func (s ValueSet) requireElementType(v Value) {
|
||||
if v.IsMarked() {
|
||||
panic("cannot store marked value directly in a set (make the set itself unknown instead)")
|
||||
}
|
||||
if !v.Type().Equals(s.ElementType()) {
|
||||
panic(fmt.Errorf("attempt to use %#v value with set of %#v", v.Type(), s.ElementType()))
|
||||
}
|
||||
}
|
255
vendor/github.com/zclconf/go-cty/cty/set_internals.go
generated
vendored
Normal file
255
vendor/github.com/zclconf/go-cty/cty/set_internals.go
generated
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
package cty
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/zclconf/go-cty/cty/set"
|
||||
)
|
||||
|
||||
// setRules provides a Rules implementation for the ./set package that
|
||||
// respects the equality rules for cty values of the given type.
|
||||
//
|
||||
// This implementation expects that values added to the set will be
|
||||
// valid internal values for the given Type, which is to say that wrapping
|
||||
// the given value in a Value struct along with the ruleset's type should
|
||||
// produce a valid, working Value.
|
||||
type setRules struct {
|
||||
Type Type
|
||||
}
|
||||
|
||||
var _ set.OrderedRules = setRules{}
|
||||
|
||||
// Hash returns a hash value for the receiver that can be used for equality
|
||||
// checks where some inaccuracy is tolerable.
|
||||
//
|
||||
// The hash function is value-type-specific, so it is not meaningful to compare
|
||||
// hash results for values of different types.
|
||||
//
|
||||
// This function is not safe to use for security-related applications, since
|
||||
// the hash used is not strong enough.
|
||||
func (val Value) Hash() int {
|
||||
hashBytes, marks := makeSetHashBytes(val)
|
||||
if len(marks) > 0 {
|
||||
panic("can't take hash of value that has marks or has embedded values that have marks")
|
||||
}
|
||||
return int(crc32.ChecksumIEEE(hashBytes))
|
||||
}
|
||||
|
||||
func (r setRules) Hash(v interface{}) int {
|
||||
return Value{
|
||||
ty: r.Type,
|
||||
v: v,
|
||||
}.Hash()
|
||||
}
|
||||
|
||||
func (r setRules) Equivalent(v1 interface{}, v2 interface{}) bool {
|
||||
v1v := Value{
|
||||
ty: r.Type,
|
||||
v: v1,
|
||||
}
|
||||
v2v := Value{
|
||||
ty: r.Type,
|
||||
v: v2,
|
||||
}
|
||||
|
||||
eqv := v1v.Equals(v2v)
|
||||
|
||||
// By comparing the result to true we ensure that an Unknown result,
|
||||
// which will result if either value is unknown, will be considered
|
||||
// as non-equivalent. Two unknown values are not equivalent for the
|
||||
// sake of set membership.
|
||||
return eqv.v == true
|
||||
}
|
||||
|
||||
// SameRules is only true if the other Rules instance is also a setRules struct,
|
||||
// and the types are considered equal.
|
||||
func (r setRules) SameRules(other set.Rules) bool {
|
||||
rules, ok := other.(setRules)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return r.Type.Equals(rules.Type)
|
||||
}
|
||||
|
||||
// Less is an implementation of set.OrderedRules so that we can iterate over
|
||||
// set elements in a consistent order, where such an order is possible.
|
||||
func (r setRules) Less(v1, v2 interface{}) bool {
|
||||
v1v := Value{
|
||||
ty: r.Type,
|
||||
v: v1,
|
||||
}
|
||||
v2v := Value{
|
||||
ty: r.Type,
|
||||
v: v2,
|
||||
}
|
||||
|
||||
if v1v.RawEquals(v2v) { // Easy case: if they are equal then v1 can't be less
|
||||
return false
|
||||
}
|
||||
|
||||
// Null values always sort after non-null values
|
||||
if v2v.IsNull() && !v1v.IsNull() {
|
||||
return true
|
||||
} else if v1v.IsNull() {
|
||||
return false
|
||||
}
|
||||
// Unknown values always sort after known values
|
||||
if v1v.IsKnown() && !v2v.IsKnown() {
|
||||
return true
|
||||
} else if !v1v.IsKnown() {
|
||||
return false
|
||||
}
|
||||
|
||||
switch r.Type {
|
||||
case String:
|
||||
// String values sort lexicographically
|
||||
return v1v.AsString() < v2v.AsString()
|
||||
case Bool:
|
||||
// Weird to have a set of bools, but if we do then false sorts before true.
|
||||
if v2v.True() || !v1v.True() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case Number:
|
||||
v1f := v1v.AsBigFloat()
|
||||
v2f := v2v.AsBigFloat()
|
||||
return v1f.Cmp(v2f) < 0
|
||||
default:
|
||||
// No other types have a well-defined ordering, so we just produce a
|
||||
// default consistent-but-undefined ordering then. This situation is
|
||||
// not considered a compatibility constraint; callers should rely only
|
||||
// on the ordering rules for primitive values.
|
||||
v1h, _ := makeSetHashBytes(v1v)
|
||||
v2h, _ := makeSetHashBytes(v2v)
|
||||
return bytes.Compare(v1h, v2h) < 0
|
||||
}
|
||||
}
|
||||
|
||||
func makeSetHashBytes(val Value) ([]byte, ValueMarks) {
|
||||
var buf bytes.Buffer
|
||||
marks := make(ValueMarks)
|
||||
appendSetHashBytes(val, &buf, marks)
|
||||
return buf.Bytes(), marks
|
||||
}
|
||||
|
||||
func appendSetHashBytes(val Value, buf *bytes.Buffer, marks ValueMarks) {
|
||||
// Exactly what bytes we generate here don't matter as long as the following
|
||||
// constraints hold:
|
||||
// - Unknown and null values all generate distinct strings from
|
||||
// each other and from any normal value of the given type.
|
||||
// - The delimiter used to separate items in a compound structure can
|
||||
// never appear literally in any of its elements.
|
||||
// Since we don't support hetrogenous lists we don't need to worry about
|
||||
// collisions between values of different types, apart from
|
||||
// PseudoTypeDynamic.
|
||||
// If in practice we *do* get a collision then it's not a big deal because
|
||||
// the Equivalent function will still distinguish values, but set
|
||||
// performance will be best if we are able to produce a distinct string
|
||||
// for each distinct value, unknown values notwithstanding.
|
||||
|
||||
// Marks aren't considered part of a value for equality-testing purposes,
|
||||
// so we'll unmark our value before we work with it but we'll remember
|
||||
// the marks in case the caller needs to re-apply them to a derived
|
||||
// value.
|
||||
if val.IsMarked() {
|
||||
unmarkedVal, valMarks := val.Unmark()
|
||||
for m := range valMarks {
|
||||
marks[m] = struct{}{}
|
||||
}
|
||||
val = unmarkedVal
|
||||
}
|
||||
|
||||
if !val.IsKnown() {
|
||||
buf.WriteRune('?')
|
||||
return
|
||||
}
|
||||
if val.IsNull() {
|
||||
buf.WriteRune('~')
|
||||
return
|
||||
}
|
||||
|
||||
switch val.ty {
|
||||
case Number:
|
||||
// Due to an unfortunate quirk of gob encoding for big.Float, we end up
|
||||
// with non-pointer values immediately after a gob round-trip, and
|
||||
// we end up in here before we've had a chance to run
|
||||
// gobDecodeFixNumberPtr on the inner values of a gob-encoded set,
|
||||
// and so sadly we must make a special effort to handle that situation
|
||||
// here just so that we can get far enough along to fix it up for
|
||||
// everything else in this package.
|
||||
if bf, ok := val.v.(big.Float); ok {
|
||||
buf.WriteString(bf.String())
|
||||
return
|
||||
}
|
||||
buf.WriteString(val.v.(*big.Float).String())
|
||||
return
|
||||
case Bool:
|
||||
if val.v.(bool) {
|
||||
buf.WriteRune('T')
|
||||
} else {
|
||||
buf.WriteRune('F')
|
||||
}
|
||||
return
|
||||
case String:
|
||||
buf.WriteString(fmt.Sprintf("%q", val.v.(string)))
|
||||
return
|
||||
}
|
||||
|
||||
if val.ty.IsMapType() {
|
||||
buf.WriteRune('{')
|
||||
val.ForEachElement(func(keyVal, elementVal Value) bool {
|
||||
appendSetHashBytes(keyVal, buf, marks)
|
||||
buf.WriteRune(':')
|
||||
appendSetHashBytes(elementVal, buf, marks)
|
||||
buf.WriteRune(';')
|
||||
return false
|
||||
})
|
||||
buf.WriteRune('}')
|
||||
return
|
||||
}
|
||||
|
||||
if val.ty.IsListType() || val.ty.IsSetType() {
|
||||
buf.WriteRune('[')
|
||||
val.ForEachElement(func(keyVal, elementVal Value) bool {
|
||||
appendSetHashBytes(elementVal, buf, marks)
|
||||
buf.WriteRune(';')
|
||||
return false
|
||||
})
|
||||
buf.WriteRune(']')
|
||||
return
|
||||
}
|
||||
|
||||
if val.ty.IsObjectType() {
|
||||
buf.WriteRune('<')
|
||||
attrNames := make([]string, 0, len(val.ty.AttributeTypes()))
|
||||
for attrName := range val.ty.AttributeTypes() {
|
||||
attrNames = append(attrNames, attrName)
|
||||
}
|
||||
sort.Strings(attrNames)
|
||||
for _, attrName := range attrNames {
|
||||
appendSetHashBytes(val.GetAttr(attrName), buf, marks)
|
||||
buf.WriteRune(';')
|
||||
}
|
||||
buf.WriteRune('>')
|
||||
return
|
||||
}
|
||||
|
||||
if val.ty.IsTupleType() {
|
||||
buf.WriteRune('<')
|
||||
val.ForEachElement(func(keyVal, elementVal Value) bool {
|
||||
appendSetHashBytes(elementVal, buf, marks)
|
||||
buf.WriteRune(';')
|
||||
return false
|
||||
})
|
||||
buf.WriteRune('>')
|
||||
return
|
||||
}
|
||||
|
||||
// should never get down here
|
||||
panic("unsupported type in set hash")
|
||||
}
|
72
vendor/github.com/zclconf/go-cty/cty/set_type.go
generated
vendored
Normal file
72
vendor/github.com/zclconf/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/zclconf/go-cty/cty/tuple_type.go
generated
vendored
Normal file
121
vendor/github.com/zclconf/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")
|
||||
}
|
161
vendor/github.com/zclconf/go-cty/cty/type.go
generated
vendored
Normal file
161
vendor/github.com/zclconf/go-cty/cty/type.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
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 {
|
||||
if t == NilType || other == NilType {
|
||||
return t == other
|
||||
}
|
||||
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 t.ElementType().HasDynamicTypes()
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// WithoutOptionalAttributesDeep returns a type equivalent to the receiver but
|
||||
// with any objects with optional attributes converted into fully concrete
|
||||
// object types. This operation is applied recursively.
|
||||
func (t Type) WithoutOptionalAttributesDeep() Type {
|
||||
switch {
|
||||
case t == DynamicPseudoType, t.IsPrimitiveType(), t.IsCapsuleType():
|
||||
return t
|
||||
case t.IsMapType():
|
||||
return Map(t.ElementType().WithoutOptionalAttributesDeep())
|
||||
case t.IsListType():
|
||||
return List(t.ElementType().WithoutOptionalAttributesDeep())
|
||||
case t.IsSetType():
|
||||
return Set(t.ElementType().WithoutOptionalAttributesDeep())
|
||||
case t.IsTupleType():
|
||||
originalElemTypes := t.TupleElementTypes()
|
||||
elemTypes := make([]Type, len(originalElemTypes))
|
||||
for i, et := range originalElemTypes {
|
||||
elemTypes[i] = et.WithoutOptionalAttributesDeep()
|
||||
}
|
||||
return Tuple(elemTypes)
|
||||
case t.IsObjectType():
|
||||
originalAttrTypes := t.AttributeTypes()
|
||||
attrTypes := make(map[string]Type, len(originalAttrTypes))
|
||||
for k, t := range originalAttrTypes {
|
||||
attrTypes[k] = t.WithoutOptionalAttributesDeep()
|
||||
}
|
||||
|
||||
// This is the subtle line which does all the work of this function: by
|
||||
// constructing a new Object type with these attribute types, we drop
|
||||
// the list of optional attributes (if present). This results in a
|
||||
// concrete Object type which requires all of the original attributes.
|
||||
return Object(attrTypes)
|
||||
default:
|
||||
// Should never happen, since above should be exhaustive
|
||||
panic("WithoutOptionalAttributesDeep does not support the given type")
|
||||
}
|
||||
}
|
||||
|
||||
type friendlyTypeNameMode rune
|
||||
|
||||
const (
|
||||
friendlyTypeName friendlyTypeNameMode = 'N'
|
||||
friendlyTypeConstraintName friendlyTypeNameMode = 'C'
|
||||
)
|
139
vendor/github.com/zclconf/go-cty/cty/type_conform.go
generated
vendored
Normal file
139
vendor/github.com/zclconf/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/zclconf/go-cty/cty/types_to_register.go
generated
vendored
Normal file
57
vendor/github.com/zclconf/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/zclconf/go-cty/cty/set"
|
||||
)
|
||||
|
||||
// InternalTypesToRegister is a slice of values that covers all of the
|
||||
// internal types used in the representation of cty.Type and cty.Value
|
||||
// across all cty Types.
|
||||
//
|
||||
// This is intended to be used to register these types with encoding
|
||||
// packages that require registration of types used in interfaces, such as
|
||||
// encoding/gob, thus allowing cty types and values to be included in streams
|
||||
// created from those packages. However, registering with gob is not necessary
|
||||
// since that is done automatically as a side-effect of importing this package.
|
||||
//
|
||||
// Callers should not do anything with the values here except pass them on
|
||||
// verbatim to a registration function.
|
||||
//
|
||||
// If the calling application uses Capsule types that wrap local structs either
|
||||
// directly or indirectly, these structs may also need to be registered in
|
||||
// order to support encoding and decoding of values of these types. That is the
|
||||
// responsibility of the calling application.
|
||||
var InternalTypesToRegister []interface{}
|
||||
|
||||
func init() {
|
||||
InternalTypesToRegister = []interface{}{
|
||||
primitiveType{},
|
||||
typeList{},
|
||||
typeMap{},
|
||||
typeObject{},
|
||||
typeSet{},
|
||||
setRules{},
|
||||
set.Set{},
|
||||
typeTuple{},
|
||||
big.Float{},
|
||||
capsuleType{},
|
||||
[]interface{}(nil),
|
||||
map[string]interface{}(nil),
|
||||
}
|
||||
|
||||
// Register these with gob here, rather than in gob.go, to ensure
|
||||
// that this will always happen after we build the above.
|
||||
for _, tv := range InternalTypesToRegister {
|
||||
typeName := fmt.Sprintf("%T", tv)
|
||||
if strings.HasPrefix(typeName, "cty.") {
|
||||
gob.RegisterName(fmt.Sprintf("github.com/zclconf/go-cty/%s", typeName), tv)
|
||||
} else {
|
||||
gob.Register(tv)
|
||||
}
|
||||
}
|
||||
}
|
85
vendor/github.com/zclconf/go-cty/cty/unknown.go
generated
vendored
Normal file
85
vendor/github.com/zclconf/go-cty/cty/unknown.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
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 used as the internal value of a
|
||||
// Value to create a placeholder for a value that isn't yet known.
|
||||
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/zclconf/go-cty/cty/unknown_as_null.go
generated
vendored
Normal file
64
vendor/github.com/zclconf/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
|
||||
}
|
142
vendor/github.com/zclconf/go-cty/cty/value.go
generated
vendored
Normal file
142
vendor/github.com/zclconf/go-cty/cty/value.go
generated
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// HasWhollyKnownType checks if the value is dynamic, or contains any nested
|
||||
// DynamicVal. This implies that both the value is not known, and the final
|
||||
// type may change.
|
||||
func (val Value) HasWhollyKnownType() bool {
|
||||
// a null dynamic type is known
|
||||
if val.IsNull() {
|
||||
return true
|
||||
}
|
||||
|
||||
// an unknown DynamicPseudoType is a DynamicVal, but we don't want to
|
||||
// check that value for equality here, since this method is used within the
|
||||
// equality check.
|
||||
if !val.IsKnown() && val.ty == DynamicPseudoType {
|
||||
return false
|
||||
}
|
||||
|
||||
if val.CanIterateElements() {
|
||||
// if the value is not known, then we can look directly at the internal
|
||||
// types
|
||||
if !val.IsKnown() {
|
||||
return !val.ty.HasDynamicTypes()
|
||||
}
|
||||
|
||||
for it := val.ElementIterator(); it.Next(); {
|
||||
_, ev := it.Element()
|
||||
if !ev.HasWhollyKnownType() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
367
vendor/github.com/zclconf/go-cty/cty/value_init.go
generated
vendored
Normal file
367
vendor/github.com/zclconf/go-cty/cty/value_init.go
generated
vendored
Normal file
@ -0,0 +1,367 @@
|
||||
package cty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/text/unicode/norm"
|
||||
|
||||
"github.com/zclconf/go-cty/cty/set"
|
||||
)
|
||||
|
||||
// BoolVal returns a Value of type Number whose internal value is the given
|
||||
// bool.
|
||||
func BoolVal(v bool) Value {
|
||||
return Value{
|
||||
ty: Bool,
|
||||
v: v,
|
||||
}
|
||||
}
|
||||
|
||||
// NumberVal returns a Value of type Number whose internal value is the given
|
||||
// big.Float. The returned value becomes the owner of the big.Float object,
|
||||
// and so it's forbidden for the caller to mutate the object after it's
|
||||
// wrapped in this way.
|
||||
func NumberVal(v *big.Float) Value {
|
||||
return Value{
|
||||
ty: Number,
|
||||
v: v,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseNumberVal returns a Value of type number produced by parsing the given
|
||||
// string as a decimal real number. To ensure that two identical strings will
|
||||
// always produce an equal number, always use this function to derive a number
|
||||
// from a string; it will ensure that the precision and rounding mode for the
|
||||
// internal big decimal is configured in a consistent way.
|
||||
//
|
||||
// If the given string cannot be parsed as a number, the returned error has
|
||||
// the message "a number is required", making it suitable to return to an
|
||||
// end-user to signal a type conversion error.
|
||||
//
|
||||
// If the given string contains a number that becomes a recurring fraction
|
||||
// when expressed in binary then it will be truncated to have a 512-bit
|
||||
// mantissa. Note that this is a higher precision than that of a float64,
|
||||
// so coverting the same decimal number first to float64 and then calling
|
||||
// NumberFloatVal will not produce an equal result; the conversion first
|
||||
// to float64 will round the mantissa to fewer than 512 bits.
|
||||
func ParseNumberVal(s string) (Value, error) {
|
||||
// Base 10, precision 512, and rounding to nearest even is the standard
|
||||
// way to handle numbers arriving as strings.
|
||||
f, _, err := big.ParseFloat(s, 10, 512, big.ToNearestEven)
|
||||
if err != nil {
|
||||
return NilVal, fmt.Errorf("a number is required")
|
||||
}
|
||||
return NumberVal(f), nil
|
||||
}
|
||||
|
||||
// MustParseNumberVal is like ParseNumberVal but it will panic in case of any
|
||||
// error. It can be used during initialization or any other situation where
|
||||
// the given string is a constant or otherwise known to be correct by the
|
||||
// caller.
|
||||
func MustParseNumberVal(s string) Value {
|
||||
ret, err := ParseNumberVal(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// NumberIntVal returns a Value of type Number whose internal value is equal
|
||||
// to the given integer.
|
||||
func NumberIntVal(v int64) Value {
|
||||
return NumberVal(new(big.Float).SetInt64(v))
|
||||
}
|
||||
|
||||
// NumberUIntVal returns a Value of type Number whose internal value is equal
|
||||
// to the given unsigned integer.
|
||||
func NumberUIntVal(v uint64) Value {
|
||||
return NumberVal(new(big.Float).SetUint64(v))
|
||||
}
|
||||
|
||||
// NumberFloatVal returns a Value of type Number whose internal value is
|
||||
// equal to the given float.
|
||||
func NumberFloatVal(v float64) Value {
|
||||
return NumberVal(new(big.Float).SetFloat64(v))
|
||||
}
|
||||
|
||||
// StringVal returns a Value of type String whose internal value is the
|
||||
// given string.
|
||||
//
|
||||
// Strings must be UTF-8 encoded sequences of valid unicode codepoints, and
|
||||
// they are NFC-normalized on entry into the world of cty values.
|
||||
//
|
||||
// If the given string is not valid UTF-8 then behavior of string operations
|
||||
// is undefined.
|
||||
func StringVal(v string) Value {
|
||||
return Value{
|
||||
ty: String,
|
||||
v: NormalizeString(v),
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizeString applies the same normalization that cty applies when
|
||||
// constructing string values.
|
||||
//
|
||||
// A return value from this function can be meaningfully compared byte-for-byte
|
||||
// with a Value.AsString result.
|
||||
func NormalizeString(s string) string {
|
||||
return norm.NFC.String(s)
|
||||
}
|
||||
|
||||
// ObjectVal returns a Value of an object type whose structure is defined
|
||||
// by the key names and value types in the given map.
|
||||
func ObjectVal(attrs map[string]Value) Value {
|
||||
attrTypes := make(map[string]Type, len(attrs))
|
||||
attrVals := make(map[string]interface{}, len(attrs))
|
||||
|
||||
for attr, val := range attrs {
|
||||
attr = NormalizeString(attr)
|
||||
attrTypes[attr] = val.ty
|
||||
attrVals[attr] = val.v
|
||||
}
|
||||
|
||||
return Value{
|
||||
ty: Object(attrTypes),
|
||||
v: attrVals,
|
||||
}
|
||||
}
|
||||
|
||||
// TupleVal returns a Value of a tuple type whose element types are
|
||||
// defined by the value types in the given slice.
|
||||
func TupleVal(elems []Value) Value {
|
||||
elemTypes := make([]Type, len(elems))
|
||||
elemVals := make([]interface{}, len(elems))
|
||||
|
||||
for i, val := range elems {
|
||||
elemTypes[i] = val.ty
|
||||
elemVals[i] = val.v
|
||||
}
|
||||
|
||||
return Value{
|
||||
ty: Tuple(elemTypes),
|
||||
v: elemVals,
|
||||
}
|
||||
}
|
||||
|
||||
// ListVal returns a Value of list type whose element type is defined by
|
||||
// the types of the given values, which must be homogenous.
|
||||
//
|
||||
// If the types are not all consistent (aside from elements that are of the
|
||||
// dynamic pseudo-type) then this function will panic. It will panic also
|
||||
// if the given list is empty, since then the element type cannot be inferred.
|
||||
// (See also ListValEmpty.)
|
||||
func ListVal(vals []Value) Value {
|
||||
if len(vals) == 0 {
|
||||
panic("must not call ListVal with empty slice")
|
||||
}
|
||||
elementType := DynamicPseudoType
|
||||
rawList := make([]interface{}, len(vals))
|
||||
|
||||
for i, val := range vals {
|
||||
if elementType == DynamicPseudoType {
|
||||
elementType = val.ty
|
||||
} else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
|
||||
panic(fmt.Errorf(
|
||||
"inconsistent list element types (%#v then %#v)",
|
||||
elementType, val.ty,
|
||||
))
|
||||
}
|
||||
|
||||
rawList[i] = val.v
|
||||
}
|
||||
|
||||
return Value{
|
||||
ty: List(elementType),
|
||||
v: rawList,
|
||||
}
|
||||
}
|
||||
|
||||
// ListValEmpty returns an empty list of the given element type.
|
||||
func ListValEmpty(element Type) Value {
|
||||
return Value{
|
||||
ty: List(element),
|
||||
v: []interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// CanListVal returns false if the given Values can not be coalesced
|
||||
// into a single List due to inconsistent element types.
|
||||
func CanListVal(vals []Value) bool {
|
||||
elementType := DynamicPseudoType
|
||||
for _, val := range vals {
|
||||
if elementType == DynamicPseudoType {
|
||||
elementType = val.ty
|
||||
} else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 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{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// CanMapVal returns false if the given Values can not be coalesced into a
|
||||
// single Map due to inconsistent element types.
|
||||
func CanMapVal(vals map[string]Value) bool {
|
||||
elementType := DynamicPseudoType
|
||||
for _, val := range vals {
|
||||
if elementType == DynamicPseudoType {
|
||||
elementType = val.ty
|
||||
} else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 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 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...)
|
||||
}
|
||||
|
||||
// CanSetVal returns false if the given Values can not be coalesced
|
||||
// into a single Set due to inconsistent element types.
|
||||
func CanSetVal(vals []Value) bool {
|
||||
elementType := DynamicPseudoType
|
||||
var markSets []ValueMarks
|
||||
|
||||
for _, val := range vals {
|
||||
if unmarkedVal, marks := val.UnmarkDeep(); len(marks) > 0 {
|
||||
val = unmarkedVal
|
||||
markSets = append(markSets, marks)
|
||||
}
|
||||
if elementType == DynamicPseudoType {
|
||||
elementType = val.ty
|
||||
} else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
1371
vendor/github.com/zclconf/go-cty/cty/value_ops.go
generated
vendored
Normal file
1371
vendor/github.com/zclconf/go-cty/cty/value_ops.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
238
vendor/github.com/zclconf/go-cty/cty/walk.go
generated
vendored
Normal file
238
vendor/github.com/zclconf/go-cty/cty/walk.go
generated
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
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
|
||||
}
|
||||
|
||||
// The callback already got a chance to see the mark in our
|
||||
// call above, so can safely strip it off here in order to
|
||||
// visit the child elements, which might still have their own marks.
|
||||
rawVal, _ := val.Unmark()
|
||||
|
||||
ty := val.Type()
|
||||
switch {
|
||||
case ty.IsObjectType():
|
||||
for it := rawVal.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 rawVal.CanIterateElements():
|
||||
for it := rawVal.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
|
||||
}
|
||||
|
||||
// Transformer is the interface used to optionally transform values in a
|
||||
// possibly-complex structure. The Enter method is called before traversing
|
||||
// through a given path, and the Exit method is called when traversal of a
|
||||
// path is complete.
|
||||
//
|
||||
// Use Enter when you want to transform a complex value before traversal
|
||||
// (preorder), and Exit when you want to transform a value after traversal
|
||||
// (postorder).
|
||||
//
|
||||
// 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.
|
||||
type Transformer interface {
|
||||
Enter(Path, Value) (Value, error)
|
||||
Exit(Path, Value) (Value, error)
|
||||
}
|
||||
|
||||
type postorderTransformer struct {
|
||||
callback func(Path, Value) (Value, error)
|
||||
}
|
||||
|
||||
func (t *postorderTransformer) Enter(p Path, v Value) (Value, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (t *postorderTransformer) Exit(p Path, v Value) (Value, error) {
|
||||
return t.callback(p, v)
|
||||
}
|
||||
|
||||
// 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 may 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, &postorderTransformer{cb})
|
||||
}
|
||||
|
||||
// TransformWithTransformer allows the caller to more closely control the
|
||||
// traversal used for transformation. See the documentation for Transformer for
|
||||
// more details.
|
||||
func TransformWithTransformer(val Value, t Transformer) (Value, error) {
|
||||
var path Path
|
||||
return transform(path, val, t)
|
||||
}
|
||||
|
||||
func transform(path Path, val Value, t Transformer) (Value, error) {
|
||||
val, err := t.Enter(path, val)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
|
||||
ty := val.Type()
|
||||
var newVal Value
|
||||
|
||||
// We need to peel off any marks here so that we can dig around
|
||||
// inside any collection values. We'll reapply these to any
|
||||
// new collections we construct, but the transformer's Exit
|
||||
// method gets the final say on what to do with those.
|
||||
rawVal, marks := val.Unmark()
|
||||
|
||||
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 := rawVal.LengthInt()
|
||||
switch l {
|
||||
case 0:
|
||||
// No deep transform for an empty sequence
|
||||
newVal = val
|
||||
default:
|
||||
elems := make([]Value, 0, l)
|
||||
for it := rawVal.ElementIterator(); it.Next(); {
|
||||
kv, ev := it.Element()
|
||||
path := append(path, IndexStep{
|
||||
Key: kv,
|
||||
})
|
||||
newEv, err := transform(path, ev, t)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
elems = append(elems, newEv)
|
||||
}
|
||||
switch {
|
||||
case ty.IsListType():
|
||||
newVal = ListVal(elems).WithMarks(marks)
|
||||
case ty.IsSetType():
|
||||
newVal = SetVal(elems).WithMarks(marks)
|
||||
case ty.IsTupleType():
|
||||
newVal = TupleVal(elems).WithMarks(marks)
|
||||
default:
|
||||
panic("unknown sequence type") // should never happen because of the case we are in
|
||||
}
|
||||
}
|
||||
|
||||
case ty.IsMapType():
|
||||
l := rawVal.LengthInt()
|
||||
switch l {
|
||||
case 0:
|
||||
// No deep transform for an empty map
|
||||
newVal = val
|
||||
default:
|
||||
elems := make(map[string]Value)
|
||||
for it := rawVal.ElementIterator(); it.Next(); {
|
||||
kv, ev := it.Element()
|
||||
path := append(path, IndexStep{
|
||||
Key: kv,
|
||||
})
|
||||
newEv, err := transform(path, ev, t)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
elems[kv.AsString()] = newEv
|
||||
}
|
||||
newVal = MapVal(elems).WithMarks(marks)
|
||||
}
|
||||
|
||||
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, t)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
newAVs[name] = newAV
|
||||
}
|
||||
newVal = ObjectVal(newAVs).WithMarks(marks)
|
||||
}
|
||||
|
||||
default:
|
||||
newVal = val
|
||||
}
|
||||
|
||||
newVal, err = t.Exit(path, newVal)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
return newVal, err
|
||||
}
|
Reference in New Issue
Block a user