mirror of https://github.com/databricks/cli.git
151 lines
3.5 KiB
Go
151 lines
3.5 KiB
Go
package generator
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
tfjson "github.com/hashicorp/terraform-json"
|
|
"github.com/iancoleman/strcase"
|
|
"github.com/zclconf/go-cty/cty"
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
type field struct {
|
|
Name string
|
|
Type string
|
|
Tag string
|
|
}
|
|
|
|
type structType struct {
|
|
Name string
|
|
Fields []field
|
|
}
|
|
|
|
// walker represents the set of types to declare to
|
|
// represent a [tfjson.SchemaBlock] as Go structs.
|
|
// See the [walk] function for usage.
|
|
type walker struct {
|
|
StructTypes []structType
|
|
}
|
|
|
|
func processAttributeType(typ cty.Type) string {
|
|
var out string
|
|
|
|
switch {
|
|
case typ.IsPrimitiveType():
|
|
switch {
|
|
case typ.Equals(cty.Bool):
|
|
out = "bool"
|
|
case typ.Equals(cty.Number):
|
|
out = "int"
|
|
case typ.Equals(cty.String):
|
|
out = "string"
|
|
default:
|
|
panic("No idea what to do for: " + typ.FriendlyName())
|
|
}
|
|
case typ.IsMapType():
|
|
out = "map[string]" + processAttributeType(*typ.MapElementType())
|
|
case typ.IsSetType():
|
|
out = "[]" + processAttributeType(*typ.SetElementType())
|
|
case typ.IsListType():
|
|
out = "[]" + processAttributeType(*typ.ListElementType())
|
|
case typ.IsObjectType():
|
|
out = "any"
|
|
default:
|
|
panic("No idea what to do for: " + typ.FriendlyName())
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func nestedBlockKeys(block *tfjson.SchemaBlock) []string {
|
|
keys := sortKeys(block.NestedBlocks)
|
|
|
|
// Remove TF specific "timeouts" block.
|
|
if i := slices.Index(keys, "timeouts"); i != -1 {
|
|
keys = slices.Delete(keys, i, i+1)
|
|
}
|
|
|
|
return keys
|
|
}
|
|
|
|
func (w *walker) walk(block *tfjson.SchemaBlock, name []string) error {
|
|
// Produce nested types before this block itself.
|
|
// This ensures types are defined before they are referenced.
|
|
for _, k := range nestedBlockKeys(block) {
|
|
v := block.NestedBlocks[k]
|
|
err := w.walk(v.Block, append(name, strcase.ToCamel(k)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Declare type.
|
|
typ := structType{
|
|
Name: strings.Join(name, ""),
|
|
}
|
|
|
|
// Declare attributes.
|
|
for _, k := range sortKeys(block.Attributes) {
|
|
v := block.Attributes[k]
|
|
|
|
// Assert the attribute type is always set.
|
|
if v.AttributeType == cty.NilType {
|
|
return fmt.Errorf("unexpected nil type for attribute %s", k)
|
|
}
|
|
|
|
// Collect field properties.
|
|
fieldName := strcase.ToCamel(k)
|
|
fieldType := processAttributeType(v.AttributeType)
|
|
fieldTag := k
|
|
if v.Required && v.Optional {
|
|
return fmt.Errorf("both required and optional are set for attribute %s", k)
|
|
}
|
|
if !v.Required {
|
|
fieldTag = fmt.Sprintf("%s,omitempty", fieldTag)
|
|
}
|
|
|
|
// Append to list of fields for type.
|
|
typ.Fields = append(typ.Fields, field{
|
|
Name: fieldName,
|
|
Type: fieldType,
|
|
Tag: fieldTag,
|
|
})
|
|
}
|
|
|
|
// Declare nested blocks.
|
|
for _, k := range nestedBlockKeys(block) {
|
|
v := block.NestedBlocks[k]
|
|
|
|
// Collect field properties.
|
|
fieldName := strcase.ToCamel(k)
|
|
fieldTypePrefix := ""
|
|
if v.MaxItems == 1 {
|
|
fieldTypePrefix = "*"
|
|
} else {
|
|
fieldTypePrefix = "[]"
|
|
}
|
|
fieldType := fmt.Sprintf("%s%s", fieldTypePrefix, strings.Join(append(name, strcase.ToCamel(k)), ""))
|
|
fieldTag := fmt.Sprintf("%s,omitempty", k)
|
|
|
|
// Append to list of fields for type.
|
|
typ.Fields = append(typ.Fields, field{
|
|
Name: fieldName,
|
|
Type: fieldType,
|
|
Tag: fieldTag,
|
|
})
|
|
}
|
|
|
|
// Append type to list of structs.
|
|
w.StructTypes = append(w.StructTypes, typ)
|
|
return nil
|
|
}
|
|
|
|
// walk recursively traverses [tfjson.SchemaBlock] and returns the
|
|
// set of types to declare to represents it as Go structs.
|
|
func walk(block *tfjson.SchemaBlock, name []string) (*walker, error) {
|
|
w := &walker{}
|
|
err := w.walk(block, name)
|
|
return w, err
|
|
}
|