1
1
Fork 0
mirror of https://github.com/mcuadros/ascode synced 2024-05-09 09:06:32 +02:00
ascode/starlark/types/type.go

155 lines
2.9 KiB
Go

package types
import (
"fmt"
"strings"
"github.com/zclconf/go-cty/cty"
"go.starlark.net/starlark"
)
// Type is a helper to manipulate and transform starlark.Type and cty.Type
type Type struct {
typ string
cty cty.Type
}
// MustTypeFromStarlark returns a Type from a given starlark type string.
// Panics if error.
func MustTypeFromStarlark(typ string) *Type {
t, err := NewTypeFromStarlark(typ)
if err != nil {
panic(err)
}
return t
}
// NewTypeFromStarlark returns a Type from a given starlark type string.
func NewTypeFromStarlark(typ string) (*Type, error) {
complex := strings.SplitN(typ, "<", 2)
if len(complex) == 2 {
typ = complex[0]
}
t := &Type{}
t.typ = typ
switch typ {
case "bool":
t.cty = cty.Bool
case "int", "float":
t.cty = cty.Number
case "string":
t.cty = cty.String
case "list", "ResourceCollection":
t.cty = cty.List(cty.NilType)
case "dict", "Resource":
t.cty = cty.Map(cty.NilType)
case "Attribute":
t.cty = cty.String
default:
return nil, fmt.Errorf("unexpected %q type", typ)
}
return t, nil
}
// MustTypeFromCty returns a Type froma given cty.Type. Panics if error.
func MustTypeFromCty(typ cty.Type) *Type {
t, err := NewTypeFromCty(typ)
if err != nil {
panic(err)
}
return t
}
// NewTypeFromCty returns a Type froma given cty.Type.
func NewTypeFromCty(typ cty.Type) (*Type, error) {
t := &Type{}
t.cty = typ
switch typ {
case cty.String:
t.typ = "string"
case cty.Number:
t.typ = "int"
case cty.Bool:
t.typ = "bool"
}
if typ.IsMapType() || typ.IsObjectType() {
t.typ = "dict"
}
if typ.IsListType() {
t.typ = "list"
}
if typ.IsSetType() {
t.typ = "set"
}
if typ.IsTupleType() {
t.typ = "tuple"
}
return t, nil
}
// Starlark returns the type as starlark type string.
func (t *Type) Starlark() string {
return t.typ
}
// Cty returns the type as cty.Type.
func (t *Type) Cty() cty.Type {
return t.cty
}
// Validate validates a value against the type.
func (t *Type) Validate(v starlark.Value) error {
switch v.(type) {
case starlark.String:
if t.cty == cty.String {
return nil
}
case starlark.Int, starlark.Float:
if t.cty == cty.Number {
return nil
}
case starlark.Bool:
if t.cty == cty.Bool {
return nil
}
case *Attribute:
if t.cty == v.(*Attribute).t {
return nil
}
vt := v.(*Attribute).InnerType().Starlark()
return fmt.Errorf("expected %s, got %s", t.typ, vt)
case *starlark.List:
if t.cty.IsListType() || t.cty.IsSetType() {
return t.validateListType(v.(*starlark.List), t.cty.ElementType())
}
case *starlark.Dict:
if t.cty.IsMapType() || t.cty.IsObjectType() {
return nil
}
}
return fmt.Errorf("expected %s, got %s", t.typ, v.Type())
}
func (t *Type) validateListType(l *starlark.List, expected cty.Type) error {
for i := 0; i < l.Len(); i++ {
if err := MustTypeFromCty(expected).Validate(l.Index(i)); err != nil {
return fmt.Errorf("index %d: %s", i, err)
}
}
return nil
}