terraform-provider-gitea/vendor/github.com/hashicorp/terraform-plugin-go/tftypes/object.go
dependabot[bot] 910ccdb092
Bump github.com/hashicorp/terraform-plugin-sdk/v2 from 2.26.1 to 2.27.0
Bumps [github.com/hashicorp/terraform-plugin-sdk/v2](https://github.com/hashicorp/terraform-plugin-sdk) from 2.26.1 to 2.27.0.
- [Release notes](https://github.com/hashicorp/terraform-plugin-sdk/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-sdk/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-sdk/compare/v2.26.1...v2.27.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-sdk/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 20:21:30 +00:00

257 lines
7.6 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package tftypes
import (
"encoding/json"
"fmt"
"sort"
"strings"
)
// Object is a Terraform type representing an unordered collection of
// attributes, potentially of differing types, each identifiable with a unique
// string name. The number of attributes, their names, and their types are part
// of the type signature for the Object, and so two Objects with different
// attribute names or types are considered to be distinct types.
type Object struct {
// AttributeTypes is a map of attributes to their types. The key should
// be the name of the attribute, and the value should be the type of
// the attribute.
AttributeTypes map[string]Type
// OptionalAttributes is a set of attributes that are optional. This
// allows values of this object type to include or not include those
// attributes without changing the type of the value; other attributes
// are considered part of the type signature, and their absence means a
// value is no longer of that type.
//
// OptionalAttributes is only valid when declaring a type constraint
// (e.g. Schema) and should not be used as part of a Type when creating
// a Value (e.g. NewValue()). When creating a Value, all OptionalAttributes
// must still be defined in the Object by setting each attribute to a null
// or known value for its attribute type.
//
// The key of OptionalAttributes should be the name of the attribute
// that is optional. The value should be an empty struct, used only to
// indicate presence.
//
// OptionalAttributes must also be listed in the AttributeTypes
// property, indicating their types.
OptionalAttributes map[string]struct{}
// 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 an Object,
// returning the Type found at that AttributePath within the Object. If the
// AttributePathStep cannot be applied to the Object, an ErrInvalidStep error
// will be returned.
func (o Object) ApplyTerraform5AttributePathStep(step AttributePathStep) (interface{}, error) {
switch s := step.(type) {
case AttributeName:
if len(o.AttributeTypes) == 0 {
return nil, ErrInvalidStep
}
attrType, ok := o.AttributeTypes[string(s)]
if !ok {
return nil, ErrInvalidStep
}
return attrType, nil
default:
return nil, ErrInvalidStep
}
}
// Equal returns true if the two Objects are exactly equal. Unlike Is, passing
// in an Object with no AttributeTypes will always return false.
func (o Object) Equal(other Type) bool {
v, ok := other.(Object)
if !ok {
return false
}
if v.AttributeTypes == nil || o.AttributeTypes == nil {
// when doing exact comparisons, we can't compare types that
// don't have attribute types set, so we just consider them not
// equal
return false
}
// if the don't have the exact same optional attributes, they're not
// the same type.
if len(v.OptionalAttributes) != len(o.OptionalAttributes) {
return false
}
for attr := range o.OptionalAttributes {
if !v.attrIsOptional(attr) {
return false
}
}
// if they don't have the same attribute types, they're not the
// same type.
if len(v.AttributeTypes) != len(o.AttributeTypes) {
return false
}
for k, typ := range o.AttributeTypes {
if _, ok := v.AttributeTypes[k]; !ok {
return false
}
if !typ.Equal(v.AttributeTypes[k]) {
return false
}
}
return true
}
// UsableAs returns whether the two Objects are type compatible.
//
// If the other type is DynamicPseudoType, it will return true.
// If the other type is not a Object, it will return false.
// If the other Object does not have matching AttributeTypes length, it will
// return false.
// If the other Object does not have a type compatible ElementType for every
// nested attribute, it will return false.
//
// If the current type contains OptionalAttributes, it will panic.
func (o Object) UsableAs(other Type) bool {
if other.Is(DynamicPseudoType) {
return true
}
v, ok := other.(Object)
if !ok {
return false
}
if len(o.OptionalAttributes) > 0 {
panic("Objects with OptionalAttributes cannot be used.")
}
if len(v.AttributeTypes) != len(o.AttributeTypes) {
return false
}
for k, typ := range o.AttributeTypes {
otherTyp, ok := v.AttributeTypes[k]
if !ok {
return false
}
if !typ.UsableAs(otherTyp) {
return false
}
}
return true
}
// Is returns whether `t` is an Object type or not. It does not perform any
// AttributeTypes checks.
func (o Object) Is(t Type) bool {
_, ok := t.(Object)
return ok
}
func (o Object) attrIsOptional(attr string) bool {
if o.OptionalAttributes == nil {
return false
}
_, ok := o.OptionalAttributes[attr]
return ok
}
func (o Object) String() string {
var res strings.Builder
res.WriteString("tftypes.Object[")
keys := make([]string, 0, len(o.AttributeTypes))
for k := range o.AttributeTypes {
keys = append(keys, k)
}
sort.Strings(keys)
for pos, key := range keys {
if pos != 0 {
res.WriteString(", ")
}
res.WriteString(`"` + key + `":`)
res.WriteString(o.AttributeTypes[key].String())
if o.attrIsOptional(key) {
res.WriteString(`?`)
}
}
res.WriteString("]")
return res.String()
}
func (o Object) private() {}
func (o Object) supportedGoTypes() []string {
return []string{"map[string]tftypes.Value"}
}
func valueFromObject(types map[string]Type, optionalAttrs map[string]struct{}, in interface{}) (Value, error) {
switch value := in.(type) {
case map[string]Value:
// types should only be null if the "Object" is actually a
// DynamicPseudoType being created from a map[string]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 {
for k := range types {
if _, ok := optionalAttrs[k]; ok {
// if it's optional, we don't need to check that it has a value
continue
}
if _, ok := value[k]; !ok {
return Value{}, fmt.Errorf("can't create a tftypes.Value of type %s, required attribute %q not set", Object{AttributeTypes: types}, k)
}
}
for k, v := range value {
typ, ok := types[k]
if !ok {
return Value{}, fmt.Errorf("can't set a value on %q in tftypes.NewValue, key not part of the object type %s", k, Object{AttributeTypes: types})
}
if v.Type() == nil {
return Value{}, NewAttributePath().WithAttributeName(k).NewErrorf("missing value type")
}
if !v.Type().UsableAs(typ) {
return Value{}, NewAttributePath().WithAttributeName(k).NewErrorf("can't use %s as %s", v.Type(), typ)
}
}
}
return Value{
typ: Object{AttributeTypes: types, OptionalAttributes: optionalAttrs},
value: value,
}, nil
default:
return Value{}, fmt.Errorf("tftypes.NewValue can't use %T as a tftypes.Object; expected types are: %s", in, formattedSupportedGoTypes(Object{}))
}
}
// MarshalJSON returns a JSON representation of the full type signature of `o`,
// including the AttributeTypes.
//
// Deprecated: this is not meant to be called by third-party code.
func (o Object) MarshalJSON() ([]byte, error) {
attrs, err := json.Marshal(o.AttributeTypes)
if err != nil {
return nil, err
}
var optionalAttrs []byte
if len(o.OptionalAttributes) > 0 {
optionalAttrs = append(optionalAttrs, []byte(",")...)
names := make([]string, 0, len(o.OptionalAttributes))
for k := range o.OptionalAttributes {
names = append(names, k)
}
sort.Strings(names)
optionalsJSON, err := json.Marshal(names)
if err != nil {
return nil, err
}
optionalAttrs = append(optionalAttrs, optionalsJSON...)
}
return []byte(`["object",` + string(attrs) + string(optionalAttrs) + `]`), nil
}