mirror of
https://github.com/mcuadros/ascode
synced 2024-11-23 01:11:59 +01:00
provider: type system
This commit is contained in:
parent
aa6b2fdde3
commit
9d2a6fd909
@ -32,7 +32,7 @@ func (c *ResourceCollection) String() string {
|
||||
|
||||
// Type honors the starlark.Value interface.
|
||||
func (c *ResourceCollection) Type() string {
|
||||
return fmt.Sprintf("%s_collection", c.typ)
|
||||
return "collection"
|
||||
}
|
||||
|
||||
// Truth honors the starlark.Value interface.
|
||||
|
30
provider/computed.go
Normal file
30
provider/computed.go
Normal file
@ -0,0 +1,30 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"go.starlark.net/starlark"
|
||||
)
|
||||
|
||||
type sString = starlark.String
|
||||
|
||||
type Computed struct {
|
||||
r *Resource
|
||||
a *configschema.Attribute
|
||||
name string
|
||||
sString
|
||||
}
|
||||
|
||||
func NewComputed(r *Resource, a *configschema.Attribute, name string) *Computed {
|
||||
return &Computed{
|
||||
r: r,
|
||||
a: a,
|
||||
name: name,
|
||||
sString: starlark.String(fmt.Sprintf("${%s.%s.%s.%s}", r.kind, r.typ, r.name, name)),
|
||||
}
|
||||
}
|
||||
|
||||
func (*Computed) Type() string {
|
||||
return "computed"
|
||||
}
|
@ -1,11 +1,8 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/hashicorp/hcl2/hclwrite"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"go.starlark.net/starlark"
|
||||
)
|
||||
|
||||
@ -51,14 +48,14 @@ func (r *Resource) ToHCL(b *hclwrite.Body) {
|
||||
}
|
||||
|
||||
body := block.Body()
|
||||
for k, attr := range r.block.Attributes {
|
||||
for k := range r.block.Attributes {
|
||||
v, ok := r.values[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO(mcuadros): I don't know how to do this properly, meanwhile, this works.
|
||||
if c, ok := v.(*Computed); ok {
|
||||
if c, ok := v.v.(*Computed); ok {
|
||||
body.SetAttributeTraversal(k, hcl.Traversal{hcl.TraverseRoot{
|
||||
Name: c.String(),
|
||||
}})
|
||||
@ -66,7 +63,7 @@ func (r *Resource) ToHCL(b *hclwrite.Body) {
|
||||
continue
|
||||
}
|
||||
|
||||
body.SetAttributeValue(k, EncodeToCty(attr.Type, ValueToNative(v)))
|
||||
body.SetAttributeValue(k, v.Cty())
|
||||
}
|
||||
|
||||
for k := range r.block.BlockTypes {
|
||||
@ -75,32 +72,8 @@ func (r *Resource) ToHCL(b *hclwrite.Body) {
|
||||
continue
|
||||
}
|
||||
|
||||
if collection, ok := v.(HCLCompatible); ok {
|
||||
if collection, ok := v.Value().(HCLCompatible); ok {
|
||||
collection.ToHCL(block.Body())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func EncodeToCty(t cty.Type, v interface{}) cty.Value {
|
||||
switch value := v.(type) {
|
||||
case string:
|
||||
return cty.StringVal(value)
|
||||
case int64:
|
||||
return cty.NumberIntVal(value)
|
||||
case bool:
|
||||
return cty.BoolVal(value)
|
||||
case []interface{}:
|
||||
if len(value) == 0 {
|
||||
return cty.ListValEmpty(t)
|
||||
}
|
||||
|
||||
values := make([]cty.Value, len(value))
|
||||
for i, v := range value {
|
||||
values[i] = EncodeToCty(t, v)
|
||||
}
|
||||
|
||||
return cty.ListVal(values)
|
||||
default:
|
||||
return cty.StringVal(fmt.Sprintf("unhandled: %T", v))
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/hashicorp/hcl2/hclwrite"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"go.starlark.net/starlark"
|
||||
)
|
||||
|
||||
@ -24,7 +23,7 @@ type Resource struct {
|
||||
typ string
|
||||
kind ResourceKind
|
||||
block *configschema.Block
|
||||
values map[string]starlark.Value
|
||||
values map[string]*Value
|
||||
}
|
||||
|
||||
func MakeResource(name, typ string, k ResourceKind, b *configschema.Block, kwargs []starlark.Tuple) (*Resource, error) {
|
||||
@ -33,7 +32,7 @@ func MakeResource(name, typ string, k ResourceKind, b *configschema.Block, kwarg
|
||||
typ: typ,
|
||||
kind: k,
|
||||
block: b,
|
||||
values: make(map[string]starlark.Value),
|
||||
values: make(map[string]*Value),
|
||||
}
|
||||
|
||||
return r, r.loadKeywordArgs(kwargs)
|
||||
@ -69,7 +68,7 @@ func (r *Resource) String() string {
|
||||
|
||||
// Type honors the starlark.Value interface.
|
||||
func (r *Resource) Type() string {
|
||||
return r.typ
|
||||
return "resource"
|
||||
}
|
||||
|
||||
// Truth honors the starlark.Value interface.
|
||||
@ -87,7 +86,7 @@ func (r *Resource) Hash() (uint32, error) {
|
||||
for name, value := range r.values {
|
||||
namehash, _ := starlark.String(name).Hash()
|
||||
x = x ^ 3*namehash
|
||||
y, err := value.Hash()
|
||||
y, err := value.Value().Hash()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -116,7 +115,7 @@ func (r *Resource) Attr(name string) (starlark.Value, error) {
|
||||
}
|
||||
|
||||
if v, ok := r.values[name]; ok {
|
||||
return v, nil
|
||||
return v.Value(), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
@ -128,15 +127,16 @@ func (r *Resource) attrComputed(name string, attr *configschema.Attribute) (star
|
||||
func (r *Resource) attrBlock(name string, b *configschema.NestedBlock) (starlark.Value, error) {
|
||||
if b.MaxItems != 1 {
|
||||
if _, ok := r.values[name]; !ok {
|
||||
r.values[name] = NewResourceCollection(name, NestedK, &b.Block)
|
||||
r.values[name] = MustValue(NewResourceCollection(name, NestedK, &b.Block))
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := r.values[name]; !ok {
|
||||
r.values[name], _ = MakeResource("", name, NestedK, &b.Block, nil)
|
||||
resource, _ := MakeResource("", name, NestedK, &b.Block, nil)
|
||||
r.values[name] = MustValue(resource)
|
||||
}
|
||||
|
||||
return r.values[name], nil
|
||||
return r.values[name].Value(), nil
|
||||
}
|
||||
|
||||
// AttrNames honors the starlark.HasAttrs interface.
|
||||
@ -169,11 +169,11 @@ func (r *Resource) SetField(name string, v starlark.Value) error {
|
||||
return starlark.NoSuchAttrError(errmsg)
|
||||
}
|
||||
|
||||
if err := ValidateType(v, attr.Type); err != nil {
|
||||
if err := MustTypeFromCty(attr.Type).Validate(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.values[name] = v
|
||||
r.values[name] = MustValue(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -190,127 +190,18 @@ func (r *Resource) setFieldFromNestedBlock(name string, b *configschema.NestedBl
|
||||
func (r *Resource) toDict() *starlark.Dict {
|
||||
d := starlark.NewDict(len(r.values))
|
||||
for k, v := range r.values {
|
||||
if r, ok := v.(*Resource); ok {
|
||||
if r, ok := v.Value().(*Resource); ok {
|
||||
d.SetKey(starlark.String(k), r.toDict())
|
||||
continue
|
||||
}
|
||||
|
||||
if r, ok := v.(*ResourceCollection); ok {
|
||||
if r, ok := v.Value().(*ResourceCollection); ok {
|
||||
d.SetKey(starlark.String(k), r.toDict())
|
||||
continue
|
||||
}
|
||||
|
||||
d.SetKey(starlark.String(k), v)
|
||||
d.SetKey(starlark.String(k), v.Value())
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func ValueToNative(v starlark.Value) interface{} {
|
||||
switch cast := v.(type) {
|
||||
case starlark.Bool:
|
||||
return bool(cast)
|
||||
case starlark.String:
|
||||
return string(cast)
|
||||
case starlark.Int:
|
||||
i, _ := cast.Int64()
|
||||
return i
|
||||
case *ResourceCollection:
|
||||
return ValueToNative(cast.List)
|
||||
case *starlark.List:
|
||||
out := make([]interface{}, cast.Len())
|
||||
for i := 0; i < cast.Len(); i++ {
|
||||
out[i] = ValueToNative(cast.Index(i))
|
||||
}
|
||||
|
||||
return out
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
NoneType # the type of None
|
||||
bool # True or False
|
||||
int # a signed integer of arbitrary magnitude
|
||||
float # an IEEE 754 double-precision floating point number
|
||||
string # a byte string
|
||||
list # a modifiable sequence of values
|
||||
tuple # an unmodifiable sequence of values
|
||||
dict # a mapping from values to values
|
||||
set # a set of values
|
||||
function # a function implemented in Starlark
|
||||
builtin_function_or_method
|
||||
*/
|
||||
|
||||
func FromStarlarkType(typ string) cty.Type {
|
||||
switch typ {
|
||||
case "bool":
|
||||
return cty.Bool
|
||||
case "int":
|
||||
case "float":
|
||||
return cty.Number
|
||||
case "string":
|
||||
return cty.String
|
||||
}
|
||||
|
||||
return cty.NilType
|
||||
}
|
||||
|
||||
func ValidateListType(l *starlark.List, expected cty.Type) error {
|
||||
for i := 0; i < l.Len(); i++ {
|
||||
if err := ValidateType(l.Index(i), expected); err != nil {
|
||||
return fmt.Errorf("index %d: %s", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateType(v starlark.Value, expected cty.Type) error {
|
||||
switch v.(type) {
|
||||
case starlark.String:
|
||||
if expected == cty.String {
|
||||
return nil
|
||||
}
|
||||
case starlark.Int:
|
||||
if expected == cty.Number {
|
||||
return nil
|
||||
}
|
||||
case starlark.Bool:
|
||||
if expected == cty.Bool {
|
||||
return nil
|
||||
}
|
||||
case *Computed:
|
||||
if expected == v.(*Computed).a.Type {
|
||||
return nil
|
||||
}
|
||||
case *starlark.List:
|
||||
if expected.IsListType() || expected.IsSetType() {
|
||||
return ValidateListType(v.(*starlark.List), expected.ElementType())
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("expected %s, got %s", ToStarlarkType(expected), v.Type())
|
||||
}
|
||||
|
||||
func ToStarlarkType(t cty.Type) string {
|
||||
switch t {
|
||||
case cty.String:
|
||||
return "string"
|
||||
case cty.Number:
|
||||
return "int"
|
||||
case cty.Bool:
|
||||
return "bool"
|
||||
}
|
||||
|
||||
if t.IsListType() {
|
||||
return "list"
|
||||
}
|
||||
|
||||
if t.IsSetType() {
|
||||
return "set"
|
||||
}
|
||||
|
||||
return "(unknown)"
|
||||
}
|
||||
|
4
provider/testdata/nested.star
vendored
4
provider/testdata/nested.star
vendored
@ -3,11 +3,11 @@ load("assert.star", "assert")
|
||||
p = provider("aws", "2.13.0")
|
||||
d = p.data.ami("foo")
|
||||
|
||||
assert.eq(type(d.filter), "filter_collection")
|
||||
assert.eq(type(d.filter), "collection")
|
||||
|
||||
bar = d.filter(name="bar", values=["qux"])
|
||||
|
||||
assert.eq(type(bar), "filter")
|
||||
assert.eq(type(bar), "resource")
|
||||
assert.eq(bar.name, "bar")
|
||||
assert.eq(bar.values, ["qux"])
|
||||
|
||||
|
2
provider/testdata/provider.star
vendored
2
provider/testdata/provider.star
vendored
@ -8,7 +8,7 @@ assert.eq(len(dir(p.resource)), 506)
|
||||
|
||||
resources = dir(p.resource)
|
||||
assert.contains(resources, "instance")
|
||||
assert.eq(type(p.resource.instance), "aws_instance_collection")
|
||||
assert.eq(type(p.resource.instance), "collection")
|
||||
|
||||
p.resource.instance("foo")
|
||||
p.resource.instance("bar")
|
||||
|
230
provider/type.go
Normal file
230
provider/type.go
Normal file
@ -0,0 +1,230 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"go.starlark.net/starlark"
|
||||
)
|
||||
|
||||
// Value is helper to manipulate and transform starlark.Value to go types and
|
||||
// cty.Value.
|
||||
type Value struct {
|
||||
t Type
|
||||
v starlark.Value
|
||||
}
|
||||
|
||||
// MustValue returns a Value from a starlark.Value, it panics if error.
|
||||
func MustValue(v starlark.Value) *Value {
|
||||
value, err := NewValue(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// NewValue returns a Value from a starlark.Value.
|
||||
func NewValue(v starlark.Value) (*Value, error) {
|
||||
t, err := NewTypeFromStarlark(v.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Value{t: *t, v: v}, nil
|
||||
}
|
||||
|
||||
// Value returns the starlark.Value.
|
||||
func (v *Value) Value() starlark.Value {
|
||||
return v.v
|
||||
}
|
||||
|
||||
// Type returns the Type of the value.
|
||||
func (v *Value) Type() *Type {
|
||||
return &v.t
|
||||
}
|
||||
|
||||
// Cty returns the cty.Value.
|
||||
func (v *Value) Cty() cty.Value {
|
||||
switch v.t.Starlark() {
|
||||
case "string":
|
||||
return cty.StringVal(v.Interface().(string))
|
||||
case "int":
|
||||
return cty.NumberIntVal(v.Interface().(int64))
|
||||
case "float":
|
||||
return cty.NumberFloatVal(v.Interface().(float64))
|
||||
case "bool":
|
||||
return cty.BoolVal(v.Interface().(bool))
|
||||
case "list":
|
||||
list := v.v.(*starlark.List)
|
||||
if list.Len() == 0 {
|
||||
return cty.ListValEmpty(v.t.Cty())
|
||||
}
|
||||
|
||||
values := make([]cty.Value, list.Len())
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
values[i] = MustValue(list.Index(i)).Cty()
|
||||
}
|
||||
|
||||
return cty.ListVal(values)
|
||||
default:
|
||||
return cty.StringVal(fmt.Sprintf("unhandled: %s", v.t.typ))
|
||||
}
|
||||
}
|
||||
|
||||
// Interface returns the value as a Go value.
|
||||
func (v *Value) Interface() interface{} {
|
||||
switch cast := v.v.(type) {
|
||||
case starlark.Bool:
|
||||
return bool(cast)
|
||||
case starlark.String:
|
||||
return string(cast)
|
||||
case starlark.Int:
|
||||
i, _ := cast.Int64()
|
||||
return i
|
||||
case starlark.Float:
|
||||
return float64(cast)
|
||||
case *ResourceCollection:
|
||||
return MustValue(cast.List).Interface()
|
||||
case *starlark.List:
|
||||
out := make([]interface{}, cast.Len())
|
||||
for i := 0; i < cast.Len(); i++ {
|
||||
out[i] = MustValue(cast.Index(i)).Interface()
|
||||
}
|
||||
|
||||
return out
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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 "collection":
|
||||
t.cty = cty.List(cty.NilType)
|
||||
case "resource":
|
||||
t.cty = cty.Map(cty.NilType)
|
||||
case "list":
|
||||
t.cty = cty.List(cty.NilType)
|
||||
case "computed":
|
||||
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.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 againts 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 *Computed:
|
||||
if t.cty == v.(*Computed).a.Type {
|
||||
return nil
|
||||
}
|
||||
case *starlark.List:
|
||||
if t.cty.IsListType() || t.cty.IsSetType() {
|
||||
return t.validateListType(v.(*starlark.List), t.cty.ElementType())
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
151
provider/type_test.go
Normal file
151
provider/type_test.go
Normal file
@ -0,0 +1,151 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestNewTypeFromStarlark(t *testing.T) {
|
||||
testCases := []struct {
|
||||
typ string
|
||||
cty cty.Type
|
||||
}{
|
||||
{"bool", cty.Bool},
|
||||
{"int", cty.Number},
|
||||
{"float", cty.Number},
|
||||
{"string", cty.String},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
typ, err := NewTypeFromStarlark(tc.typ)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, typ.Cty(), tc.cty)
|
||||
}
|
||||
}
|
||||
func TestNewTypeFromStarlark_NonScalar(t *testing.T) {
|
||||
typ := MustTypeFromStarlark("list")
|
||||
assert.True(t, typ.Cty().IsListType())
|
||||
|
||||
typ = MustTypeFromStarlark("collection")
|
||||
assert.True(t, typ.Cty().IsListType())
|
||||
|
||||
typ = MustTypeFromStarlark("resource")
|
||||
assert.True(t, typ.Cty().IsMapType())
|
||||
}
|
||||
|
||||
func TestNewTypeFromCty(t *testing.T) {
|
||||
testCases := []struct {
|
||||
typ string
|
||||
cty cty.Type
|
||||
}{
|
||||
{"string", cty.String},
|
||||
{"int", cty.Number},
|
||||
{"bool", cty.Bool},
|
||||
{"list", cty.List(cty.String)},
|
||||
{"set", cty.Set(cty.String)},
|
||||
{"tuple", cty.Tuple([]cty.Type{})},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
typ, err := NewTypeFromCty(tc.cty)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, typ.Starlark(), tc.typ)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeValidate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
t string
|
||||
v starlark.Value
|
||||
err bool
|
||||
}{
|
||||
{"string", starlark.String("foo"), false},
|
||||
{"int", starlark.String("foo"), true},
|
||||
{"int", starlark.MakeInt(42), false},
|
||||
{"int", starlark.MakeInt64(42), false},
|
||||
{"string", starlark.MakeInt(42), true},
|
||||
{"int", starlark.Float(42.), false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
typ := MustTypeFromStarlark(tc.t)
|
||||
err := typ.Validate(tc.v)
|
||||
if tc.err {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeValidate_List(t *testing.T) {
|
||||
typ := MustTypeFromCty(cty.List(cty.String))
|
||||
err := typ.Validate(starlark.NewList([]starlark.Value{
|
||||
starlark.String("foo"),
|
||||
starlark.String("bar"),
|
||||
}))
|
||||
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestTypeValidate_ListError(t *testing.T) {
|
||||
typ := MustTypeFromCty(cty.List(cty.Number))
|
||||
err := typ.Validate(starlark.NewList([]starlark.Value{
|
||||
starlark.MakeInt(42),
|
||||
starlark.String("bar"),
|
||||
}))
|
||||
|
||||
assert.Errorf(t, err, "index 1: expected int, got string")
|
||||
}
|
||||
|
||||
func TestMustValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
v starlark.Value
|
||||
cty cty.Type
|
||||
value cty.Value
|
||||
native interface{}
|
||||
}{
|
||||
{
|
||||
starlark.String("foo"),
|
||||
cty.String,
|
||||
cty.StringVal("foo"),
|
||||
"foo",
|
||||
},
|
||||
{
|
||||
starlark.MakeInt(42),
|
||||
cty.Number,
|
||||
cty.NumberIntVal(42),
|
||||
int64(42),
|
||||
},
|
||||
{
|
||||
starlark.Float(42),
|
||||
cty.Number,
|
||||
cty.NumberFloatVal(42),
|
||||
42.,
|
||||
},
|
||||
{
|
||||
starlark.Bool(true),
|
||||
cty.Bool,
|
||||
cty.True,
|
||||
true,
|
||||
},
|
||||
{
|
||||
starlark.NewList([]starlark.Value{starlark.String("foo")}),
|
||||
cty.List(cty.NilType),
|
||||
cty.ListVal([]cty.Value{cty.StringVal("foo")}),
|
||||
[]interface{}{"foo"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
value := MustValue(tc.v)
|
||||
assert.Equal(t, value.Type().Cty(), tc.cty)
|
||||
assert.Equal(t, value.Value(), tc.v)
|
||||
assert.Equal(t, value.Cty(), tc.value)
|
||||
assert.Equal(t, value.Interface(), tc.native)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user