gozcl: DecodeExpression implementation

This is really just a wrapper around converting the value to a Go native
value with gocty, but is convenient to provide since it can attempt
type conversion and produce diagnostics automatically.
This commit is contained in:
Martin Atkins 2017-05-21 08:47:05 -07:00
parent bb5044d015
commit 5b8d54380b
2 changed files with 132 additions and 2 deletions

View File

@ -1,6 +1,10 @@
package gozcl
import (
"fmt"
"github.com/apparentlymart/go-cty/cty/convert"
"github.com/apparentlymart/go-cty/cty/gocty"
"github.com/apparentlymart/go-zcl/zcl"
)
@ -40,6 +44,35 @@ func DecodeBody(body zcl.Body, ctx *zcl.EvalContext, val interface{}) zcl.Diagno
// may still be accessed by a careful caller for static analysis and editor
// integration use-cases.
func DecodeExpression(expr zcl.Expression, ctx *zcl.EvalContext, val interface{}) zcl.Diagnostics {
// TODO: Implement
return nil
srcVal, diags := expr.Value(ctx)
convTy, err := gocty.ImpliedType(val)
if err != nil {
panic(fmt.Sprintf("unsuitable DecodeExpression target: %s", err))
}
srcVal, err = convert.Convert(srcVal, convTy)
if err != nil {
diags = append(diags, &zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Unsuitable value type",
Detail: fmt.Sprintf("Incorrect value type: %s", err.Error()),
Subject: expr.StartRange().Ptr(),
Context: expr.Range().Ptr(),
})
return diags
}
err = gocty.FromCtyValue(srcVal, val)
if err != nil {
diags = append(diags, &zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Unsuitable value type",
Detail: fmt.Sprintf("Incorrect value type: %s", err.Error()),
Subject: expr.StartRange().Ptr(),
Context: expr.Range().Ptr(),
})
}
return diags
}

97
gozcl/decode_test.go Normal file
View File

@ -0,0 +1,97 @@
package gozcl
import (
"fmt"
"reflect"
"testing"
"github.com/apparentlymart/go-cty/cty"
"github.com/apparentlymart/go-zcl/zcl"
)
func TestDecodeExpression(t *testing.T) {
tests := []struct {
Value cty.Value
Target interface{}
Want interface{}
DiagCount int
}{
{
cty.StringVal("hello"),
"",
"hello",
0,
},
{
cty.StringVal("hello"),
cty.NilVal,
cty.StringVal("hello"),
0,
},
{
cty.NumberIntVal(2),
"",
"2",
0,
},
{
cty.StringVal("true"),
false,
true,
0,
},
{
cty.NullVal(cty.String),
"",
"",
1, // null value is not allowed
},
{
cty.UnknownVal(cty.String),
"",
"",
1, // value must be known
},
{
cty.ListVal([]cty.Value{cty.True}),
false,
false,
1, // bool required
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
expr := &fixedExpression{test.Value}
targetVal := reflect.New(reflect.TypeOf(test.Target))
diags := DecodeExpression(expr, nil, targetVal.Interface())
if len(diags) != test.DiagCount {
t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
for _, diag := range diags {
t.Logf(" - %s", diag.Error())
}
}
got := targetVal.Elem().Interface()
if !reflect.DeepEqual(got, test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
type fixedExpression struct {
val cty.Value
}
func (e *fixedExpression) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
return e.val, nil
}
func (e *fixedExpression) Range() (r zcl.Range) {
return
}
func (e *fixedExpression) StartRange() (r zcl.Range) {
return
}