cue: allow a Value to be passed to Fill
Change-Id: I31c71e5a033339596b7827d0f4468b0ad82f9c54
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5186
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/go.go b/cue/go.go
index ae21031..f47eee8 100644
--- a/cue/go.go
+++ b/cue/go.go
@@ -214,6 +214,10 @@
}
return &nullLit{src.base()}
+ case *ast.File:
+ x := newVisitorCtx(ctx, nil, nil, nil, false)
+ return ctx.manifest(x.walk(v))
+
case ast.Expr:
x := newVisitorCtx(ctx, nil, nil, nil, false)
return ctx.manifest(x.walk(v))
diff --git a/cue/instance.go b/cue/instance.go
index 337acb0..92b72fe 100644
--- a/cue/instance.go
+++ b/cue/instance.go
@@ -334,29 +334,48 @@
// Fill creates a new instance with the values of the old instance unified with
// the given value. It is not possible to update the emit value.
+//
+// Values may be any Go value that can be converted to CUE, an ast.Expr or
+// a Value. In the latter case, it will panic if the Value is not from the same
+// Runtime.
func (inst *Instance) Fill(x interface{}, path ...string) (*Instance, error) {
ctx := inst.newContext()
root := ctx.manifest(inst.rootValue)
for i := len(path) - 1; i >= 0; i-- {
x = map[string]interface{}{path[i]: x}
}
- value := convert(ctx, root, true, x)
+ var value evaluated
+ if v, ok := x.(Value); ok {
+ if inst.index != v.ctx().index {
+ panic("value of type Value is not created with same Runtime as Instance")
+ }
+ value = v.eval(ctx)
+ } else {
+ value = convert(ctx, root, true, x)
+ }
eval := binOp(ctx, baseValue{}, opUnify, root, value)
// TODO: validate recursively?
err := inst.Err
var st *structLit
+ var stVal evaluated
switch x := eval.(type) {
case *structLit:
st = x
+ stVal = x
default:
// This should not happen.
- err = errors.Newf(x.Pos(), "error filling struct")
+ b := ctx.mkErr(eval, "error filling struct")
+ err = inst.Value().toErr(b)
+ st = &structLit{emit: b}
+ stVal = b
case *bottom:
err = inst.Value().toErr(x)
+ st = &structLit{emit: x}
+ stVal = x
}
inst = inst.index.addInst(&Instance{
rootStruct: st,
- rootValue: st,
+ rootValue: stVal,
inst: nil,
// Omit ImportPath to indicate this is not an importable package.
diff --git a/cue/types.go b/cue/types.go
index a013eb0..0f8da53 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -1361,6 +1361,10 @@
// Fill creates a new value by unifying v with the value of x at the given path.
//
+// Values may be any Go value that can be converted to CUE, an ast.Expr or
+// a Value. In the latter case, it will panic if the Value is not from the same
+// Runtime.
+//
// Any reference in v referring to the value at the given path will resolve
// to x in the newly created value. The resulting value is not validated.
func (v Value) Fill(x interface{}, path ...string) Value {
@@ -1372,7 +1376,15 @@
for i := len(path) - 1; i >= 0; i-- {
x = map[string]interface{}{path[i]: x}
}
- value := convert(ctx, root, true, x)
+ var value evaluated
+ if v, ok := x.(Value); ok {
+ if ctx.index != v.ctx().index {
+ panic("value of type Value is not created with same Runtime as Instance")
+ }
+ value = v.eval(ctx)
+ } else {
+ value = convert(ctx, root, true, x)
+ }
a := v.path.arc
a.v = mkBin(ctx, v.Pos(), opUnify, root, value)
a.cache = a.v.evalPartial(ctx)
diff --git a/cue/types_test.go b/cue/types_test.go
index 3d2ae62..aa90cd5 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -17,7 +17,6 @@
import (
"bytes"
"fmt"
- "go/parser"
"io/ioutil"
"math"
"math/big"
@@ -691,7 +690,6 @@
}
func TestLookup(t *testing.T) {
- _ = parser.ParseFile
var runtime = new(Runtime)
inst, err := runtime.Compile("x.cue", `
V :: {
@@ -774,6 +772,13 @@
}
func TestFill(t *testing.T) {
+ r := &Runtime{}
+
+ inst, err := r.CompileExpr(ast.NewStruct("bar", ast.NewString("baz")))
+ if err != nil {
+ t.Fatal(err)
+ }
+
testCases := []struct {
in string
x interface{}
@@ -799,6 +804,15 @@
out: `
"foo"
`,
+ }, {
+ in: `
+ foo: _
+ `,
+ x: inst.Value(),
+ path: "foo",
+ out: `
+ {foo: {bar: "baz"}}
+ `,
}}
for _, tc := range testCases {
@@ -807,7 +821,6 @@
path = strings.Split(tc.path, ",")
}
- r := &Runtime{}
v := compile(t, r, tc.in).Value().Fill(tc.x, path...)
w := compile(t, r, tc.out).Value()