terraform-provider-gitea/vendor/github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert/schema.go
Tobias Trabelsi e1266ebf64
Some checks reported errors
continuous-integration/drone/pr Build encountered an error
continuous-integration/drone/push Build encountered an error
updated GHA
Update to v2 SDK
updated dependencies
2022-08-06 16:21:18 +02:00

302 lines
8.4 KiB
Go

package convert
import (
"context"
"fmt"
"reflect"
"sort"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema"
"github.com/hashicorp/terraform-plugin-sdk/v2/internal/logging"
)
func tftypeFromCtyType(in cty.Type) (tftypes.Type, error) {
switch {
case in.Equals(cty.String):
return tftypes.String, nil
case in.Equals(cty.Number):
return tftypes.Number, nil
case in.Equals(cty.Bool):
return tftypes.Bool, nil
case in.Equals(cty.DynamicPseudoType):
return tftypes.DynamicPseudoType, nil
case in.IsSetType():
elemType, err := tftypeFromCtyType(in.ElementType())
if err != nil {
return nil, err
}
return tftypes.Set{
ElementType: elemType,
}, nil
case in.IsListType():
elemType, err := tftypeFromCtyType(in.ElementType())
if err != nil {
return nil, err
}
return tftypes.List{
ElementType: elemType,
}, nil
case in.IsTupleType():
elemTypes := make([]tftypes.Type, 0, in.Length())
for _, typ := range in.TupleElementTypes() {
elemType, err := tftypeFromCtyType(typ)
if err != nil {
return nil, err
}
elemTypes = append(elemTypes, elemType)
}
return tftypes.Tuple{
ElementTypes: elemTypes,
}, nil
case in.IsMapType():
elemType, err := tftypeFromCtyType(in.ElementType())
if err != nil {
return nil, err
}
return tftypes.Map{
ElementType: elemType,
}, nil
case in.IsObjectType():
attrTypes := make(map[string]tftypes.Type)
for key, typ := range in.AttributeTypes() {
attrType, err := tftypeFromCtyType(typ)
if err != nil {
return nil, err
}
attrTypes[key] = attrType
}
return tftypes.Object{
AttributeTypes: attrTypes,
}, nil
}
return nil, fmt.Errorf("unknown cty type %s", in.GoString())
}
func ctyTypeFromTFType(in tftypes.Type) (cty.Type, error) {
switch {
case in.Is(tftypes.String):
return cty.String, nil
case in.Is(tftypes.Bool):
return cty.Bool, nil
case in.Is(tftypes.Number):
return cty.Number, nil
case in.Is(tftypes.DynamicPseudoType):
return cty.DynamicPseudoType, nil
case in.Is(tftypes.List{}):
elemType, err := ctyTypeFromTFType(in.(tftypes.List).ElementType)
if err != nil {
return cty.Type{}, err
}
return cty.List(elemType), nil
case in.Is(tftypes.Set{}):
elemType, err := ctyTypeFromTFType(in.(tftypes.Set).ElementType)
if err != nil {
return cty.Type{}, err
}
return cty.Set(elemType), nil
case in.Is(tftypes.Map{}):
elemType, err := ctyTypeFromTFType(in.(tftypes.Map).ElementType)
if err != nil {
return cty.Type{}, err
}
return cty.Map(elemType), nil
case in.Is(tftypes.Tuple{}):
elemTypes := make([]cty.Type, 0, len(in.(tftypes.Tuple).ElementTypes))
for _, typ := range in.(tftypes.Tuple).ElementTypes {
elemType, err := ctyTypeFromTFType(typ)
if err != nil {
return cty.Type{}, err
}
elemTypes = append(elemTypes, elemType)
}
return cty.Tuple(elemTypes), nil
case in.Is(tftypes.Object{}):
attrTypes := make(map[string]cty.Type, len(in.(tftypes.Object).AttributeTypes))
for k, v := range in.(tftypes.Object).AttributeTypes {
attrType, err := ctyTypeFromTFType(v)
if err != nil {
return cty.Type{}, err
}
attrTypes[k] = attrType
}
return cty.Object(attrTypes), nil
}
return cty.Type{}, fmt.Errorf("unknown tftypes.Type %s", in)
}
// ConfigSchemaToProto takes a *configschema.Block and converts it to a
// tfprotov5.SchemaBlock for a grpc response.
func ConfigSchemaToProto(ctx context.Context, b *configschema.Block) *tfprotov5.SchemaBlock {
block := &tfprotov5.SchemaBlock{
Description: b.Description,
DescriptionKind: protoStringKind(ctx, b.DescriptionKind),
Deprecated: b.Deprecated,
}
for _, name := range sortedKeys(b.Attributes) {
a := b.Attributes[name]
attr := &tfprotov5.SchemaAttribute{
Name: name,
Description: a.Description,
DescriptionKind: protoStringKind(ctx, a.DescriptionKind),
Optional: a.Optional,
Computed: a.Computed,
Required: a.Required,
Sensitive: a.Sensitive,
Deprecated: a.Deprecated,
}
var err error
attr.Type, err = tftypeFromCtyType(a.Type)
if err != nil {
panic(err)
}
block.Attributes = append(block.Attributes, attr)
}
for _, name := range sortedKeys(b.BlockTypes) {
b := b.BlockTypes[name]
block.BlockTypes = append(block.BlockTypes, protoSchemaNestedBlock(ctx, name, b))
}
return block
}
func protoStringKind(ctx context.Context, k configschema.StringKind) tfprotov5.StringKind {
switch k {
default:
logging.HelperSchemaTrace(ctx, fmt.Sprintf("Unexpected configschema.StringKind: %d", k))
return tfprotov5.StringKindPlain
case configschema.StringPlain:
return tfprotov5.StringKindPlain
case configschema.StringMarkdown:
return tfprotov5.StringKindMarkdown
}
}
func protoSchemaNestedBlock(ctx context.Context, name string, b *configschema.NestedBlock) *tfprotov5.SchemaNestedBlock {
var nesting tfprotov5.SchemaNestedBlockNestingMode
switch b.Nesting {
case configschema.NestingSingle:
nesting = tfprotov5.SchemaNestedBlockNestingModeSingle
case configschema.NestingGroup:
nesting = tfprotov5.SchemaNestedBlockNestingModeGroup
case configschema.NestingList:
nesting = tfprotov5.SchemaNestedBlockNestingModeList
case configschema.NestingSet:
nesting = tfprotov5.SchemaNestedBlockNestingModeSet
case configschema.NestingMap:
nesting = tfprotov5.SchemaNestedBlockNestingModeMap
default:
nesting = tfprotov5.SchemaNestedBlockNestingModeInvalid
}
return &tfprotov5.SchemaNestedBlock{
TypeName: name,
Block: ConfigSchemaToProto(ctx, &b.Block),
Nesting: nesting,
MinItems: int64(b.MinItems),
MaxItems: int64(b.MaxItems),
}
}
// ProtoToConfigSchema takes the GetSchema_Block from a grpc response and converts it
// to a terraform *configschema.Block.
func ProtoToConfigSchema(ctx context.Context, b *tfprotov5.SchemaBlock) *configschema.Block {
block := &configschema.Block{
Attributes: make(map[string]*configschema.Attribute),
BlockTypes: make(map[string]*configschema.NestedBlock),
Description: b.Description,
DescriptionKind: schemaStringKind(ctx, b.DescriptionKind),
Deprecated: b.Deprecated,
}
for _, a := range b.Attributes {
attr := &configschema.Attribute{
Description: a.Description,
DescriptionKind: schemaStringKind(ctx, a.DescriptionKind),
Required: a.Required,
Optional: a.Optional,
Computed: a.Computed,
Sensitive: a.Sensitive,
Deprecated: a.Deprecated,
}
var err error
attr.Type, err = ctyTypeFromTFType(a.Type)
if err != nil {
panic(err)
}
block.Attributes[a.Name] = attr
}
for _, b := range b.BlockTypes {
block.BlockTypes[b.TypeName] = schemaNestedBlock(ctx, b)
}
return block
}
func schemaStringKind(ctx context.Context, k tfprotov5.StringKind) configschema.StringKind {
switch k {
default:
logging.HelperSchemaTrace(ctx, fmt.Sprintf("Unexpected tfprotov5.StringKind: %d", k))
return configschema.StringPlain
case tfprotov5.StringKindPlain:
return configschema.StringPlain
case tfprotov5.StringKindMarkdown:
return configschema.StringMarkdown
}
}
func schemaNestedBlock(ctx context.Context, b *tfprotov5.SchemaNestedBlock) *configschema.NestedBlock {
var nesting configschema.NestingMode
switch b.Nesting {
case tfprotov5.SchemaNestedBlockNestingModeSingle:
nesting = configschema.NestingSingle
case tfprotov5.SchemaNestedBlockNestingModeGroup:
nesting = configschema.NestingGroup
case tfprotov5.SchemaNestedBlockNestingModeList:
nesting = configschema.NestingList
case tfprotov5.SchemaNestedBlockNestingModeMap:
nesting = configschema.NestingMap
case tfprotov5.SchemaNestedBlockNestingModeSet:
nesting = configschema.NestingSet
default:
// In all other cases we'll leave it as the zero value (invalid) and
// let the caller validate it and deal with this.
}
nb := &configschema.NestedBlock{
Nesting: nesting,
MinItems: int(b.MinItems),
MaxItems: int(b.MaxItems),
}
nested := ProtoToConfigSchema(ctx, b.Block)
nb.Block = *nested
return nb
}
// sortedKeys returns the lexically sorted keys from the given map. This is
// used to make schema conversions are deterministic. This panics if map keys
// are not a string.
func sortedKeys(m interface{}) []string {
v := reflect.ValueOf(m)
keys := make([]string, v.Len())
mapKeys := v.MapKeys()
for i, k := range mapKeys {
keys[i] = k.Interface().(string)
}
sort.Strings(keys)
return keys
}