154 lines
4.3 KiB
Go
154 lines
4.3 KiB
Go
package tftypes
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Tuple is a Terraform type representing an ordered collection of elements,
|
|
// potentially of differing types. The number of elements and their types are
|
|
// part of the type signature for the Tuple, and so two Tuples with different
|
|
// numbers or types of elements are considered to be distinct types.
|
|
type Tuple struct {
|
|
ElementTypes []Type
|
|
|
|
// used to make this type uncomparable
|
|
// see https://golang.org/ref/spec#Comparison_operators
|
|
// this enforces the use of Is, instead
|
|
_ []struct{}
|
|
}
|
|
|
|
// ApplyTerraform5AttributePathStep applies an AttributePathStep to a Tuple,
|
|
// returning the Type found at that AttributePath within the Tuple. If the
|
|
// AttributePathStep cannot be applied to the Tuple, an ErrInvalidStep error
|
|
// will be returned.
|
|
func (tu Tuple) ApplyTerraform5AttributePathStep(step AttributePathStep) (interface{}, error) {
|
|
switch s := step.(type) {
|
|
case ElementKeyInt:
|
|
if int64(s) < 0 || int64(s) >= int64(len(tu.ElementTypes)) {
|
|
return nil, ErrInvalidStep
|
|
}
|
|
|
|
return tu.ElementTypes[int64(s)], nil
|
|
default:
|
|
return nil, ErrInvalidStep
|
|
}
|
|
}
|
|
|
|
// Equal returns true if the two Tuples are exactly equal. Unlike Is, passing
|
|
// in a Tuple with no ElementTypes will always return false.
|
|
func (tu Tuple) Equal(o Type) bool {
|
|
v, ok := o.(Tuple)
|
|
if !ok {
|
|
return false
|
|
}
|
|
if v.ElementTypes == nil || tu.ElementTypes == nil {
|
|
// when doing exact comparisons, we can't compare types that
|
|
// don't have element types set, so we just consider them not
|
|
// equal
|
|
return false
|
|
}
|
|
if len(v.ElementTypes) != len(tu.ElementTypes) {
|
|
return false
|
|
}
|
|
for pos, typ := range tu.ElementTypes {
|
|
if !typ.Equal(v.ElementTypes[pos]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// UsableAs returns whether the two Tuples are type compatible.
|
|
//
|
|
// If the other type is DynamicPseudoType, it will return true.
|
|
// If the other type is not a Tuple, it will return false.
|
|
// If the other Tuple does not have matching ElementTypes length, it will
|
|
// return false.
|
|
// If the other Tuple does not have type compatible ElementTypes in each
|
|
// position, it will return false.
|
|
func (tu Tuple) UsableAs(o Type) bool {
|
|
if o.Is(DynamicPseudoType) {
|
|
return true
|
|
}
|
|
v, ok := o.(Tuple)
|
|
if !ok {
|
|
return false
|
|
}
|
|
if len(v.ElementTypes) != len(tu.ElementTypes) {
|
|
return false
|
|
}
|
|
for pos, typ := range tu.ElementTypes {
|
|
if !typ.UsableAs(v.ElementTypes[pos]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Is returns whether `t` is a Tuple type or not. It does not perform any
|
|
// ElementTypes checks.
|
|
func (tu Tuple) Is(t Type) bool {
|
|
_, ok := t.(Tuple)
|
|
return ok
|
|
}
|
|
|
|
func (tu Tuple) String() string {
|
|
var res strings.Builder
|
|
res.WriteString("tftypes.Tuple[")
|
|
for pos, t := range tu.ElementTypes {
|
|
if pos != 0 {
|
|
res.WriteString(", ")
|
|
}
|
|
res.WriteString(t.String())
|
|
}
|
|
res.WriteString("]")
|
|
return res.String()
|
|
}
|
|
|
|
func (tu Tuple) private() {}
|
|
|
|
func (tu Tuple) supportedGoTypes() []string {
|
|
return []string{"[]tftypes.Value"}
|
|
}
|
|
|
|
func valueFromTuple(types []Type, in interface{}) (Value, error) {
|
|
switch value := in.(type) {
|
|
case []Value:
|
|
// types should only be null if the "Tuple" is actually a
|
|
// DynamicPseudoType being created from a []Value. In which
|
|
// case, we don't know what types it should have, or even how
|
|
// many there will be, so let's not validate that at all
|
|
if types != nil {
|
|
if len(types) != len(value) {
|
|
return Value{}, fmt.Errorf("can't create a tftypes.Value with %d elements, type %s requires %d elements", len(value), Tuple{ElementTypes: types}, len(types))
|
|
}
|
|
for pos, v := range value {
|
|
typ := types[pos]
|
|
if !v.Type().UsableAs(typ) {
|
|
return Value{}, NewAttributePath().WithElementKeyInt(pos).NewErrorf("can't use %s as %s", v.Type(), typ)
|
|
}
|
|
}
|
|
}
|
|
return Value{
|
|
typ: Tuple{ElementTypes: types},
|
|
value: value,
|
|
}, nil
|
|
default:
|
|
return Value{}, fmt.Errorf("tftypes.NewValue can't use %T as a tftypes.Tuple; expected types are: %s", in, formattedSupportedGoTypes(Tuple{}))
|
|
}
|
|
}
|
|
|
|
// MarshalJSON returns a JSON representation of the full type signature of
|
|
// `tu`, including the ElementTypes.
|
|
//
|
|
// Deprecated: this is not meant to be called by third-party code.
|
|
func (tu Tuple) MarshalJSON() ([]byte, error) {
|
|
elements, err := json.Marshal(tu.ElementTypes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return []byte(`["tuple",` + string(elements) + `]`), nil
|
|
}
|