Skip to content

Commit

Permalink
Change type notation to HCL2 type annotation
Browse files Browse the repository at this point in the history
Fixes #9

Terraform v0.12 introduced a new `SchemaConfigModeAttr` feature.
Most attributes have simple types, but if `SchemaConfigModeAttr` is set for
an attribute, it is syntactically NestedBlock but semantically interpreted
as an Attribute. In this case, Attribute has a complex data type. It is
reasonable to use the same notation as the type annotation in HCL2 to
represent the correct data type. However, it seems that HCL2 has a type
annotation parser but no writer, so we implement it by ourselves.

See also:
- #9
- hashicorp/terraform-provider-aws#8187
- hashicorp/terraform#20626
- https://www.terraform.io/docs/configuration/types.html

This is a breaking change caused by the upstream,
so I think it is impossible to maintain tfschema backward compatibility.
I tried to be as consistent as possible.
  • Loading branch information
minamijoyo committed May 21, 2019
1 parent d0222d5 commit 7c6105b
Showing 1 changed file with 95 additions and 5 deletions.
100 changes: 95 additions & 5 deletions tfschema/type.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package tfschema

import (
"fmt"
"sort"
"strings"

"github.com/zclconf/go-cty/cty"
Expand Down Expand Up @@ -30,11 +32,99 @@ func (t *Type) MarshalJSON() ([]byte, error) {
}

// Name returns a name of type.
// This method customize cty.GoString() to make it easy to read.
// Terraform v0.12 introduced a new `SchemaConfigModeAttr` feature.
// Most attributes have simple types, but if `SchemaConfigModeAttr` is set for
// an attribute, it is syntactically NestedBlock but semantically interpreted
// as an Attribute. In this case, Attribute has a complex data type. It is
// reasonable to use the same notation as the type annotation in HCL2 to
// represent the correct data type. However, it seems that HCL2 has a type
// annotation parser but no writer, so we implement it by ourselves.
//
// See also:
// - https://github.com/minamijoyo/tfschema/issues/9
// - https://github.com/terraform-providers/terraform-provider-aws/pull/8187
// - https://github.com/hashicorp/terraform/pull/20626
// - https://www.terraform.io/docs/configuration/types.html
func (t *Type) Name() (string, error) {
goString := t.GoString()
// drop `cty.` prefix for simplicity. (e.g. cty.String => String)
name := strings.Replace(goString, "cty.", "", -1)
switch {
case t.IsPrimitiveType():
switch t.Type {
case cty.String:
return "string", nil
case cty.Number:
return "number", nil
case cty.Bool:
return "bool", nil
}

return name, nil
case t.IsListType():
elementType := NewType(t.ElementType())
elementName, err := elementType.Name()
if err != nil {
return "", err
}
return "list(" + elementName + ")", nil

case t.IsSetType():
elementType := NewType(t.ElementType())
elementName, err := elementType.Name()
if err != nil {
return "", err
}
return "set(" + elementName + ")", nil

case t.IsMapType():
elementType := NewType(t.ElementType())
elementName, err := elementType.Name()
if err != nil {
return "", err
}
return "map(" + elementName + ")", nil

case t.IsTupleType():
elementTypes := t.TupleElementTypes()
elementNames := make([]string, 0, len(elementTypes))
for i := range elementTypes {
elementType := NewType(t.TupleElementType(i))
elementName, err := elementType.Name()
if err != nil {
return "", err
}

elementNames = append(elementNames, elementName)
}
return "tuple([ " + strings.Join(elementNames, ", ") + " ])", nil
case t.IsObjectType():
attributeTypes := t.AttributeTypes()
attributeNames := make([]string, 0, len(attributeTypes))
for k := range attributeTypes {
attributeNames = append(attributeNames, k)
}
sort.Strings(attributeNames)

attributes := make([]string, 0, len(attributeTypes))
for _, k := range attributeNames {
elementType := NewType(t.AttributeType(k))
elementName, err := elementType.Name()
if err != nil {
return "", err
}

attributes = append(attributes, k+"="+elementName)
}
return "object({ " + strings.Join(attributes, ", ") + " })", nil

case t.IsCapsuleType():
// We notice that there is a capsule type as a cty specification,
// but we don't know how to handle it properly,
// so we should make an error for now..
return "", fmt.Errorf("Failed to get name for unsupported capsule type: %#v", t)

default:
// should never happen
return "", fmt.Errorf("Failed to get name for unknown type: %#v", t)
}

// should never happen
return "", fmt.Errorf("Failed to get name for type: %#v", t)
}

0 comments on commit 7c6105b

Please sign in to comment.