cmd/cue/cmd: use unify semantics merging across packages
This includes two changes:
1) Use Unify instead of Fill to merge values
2) Change instance iteration to value iteration.
Instead of merging all files into a single file using the
semantics of embedding, we should use that of
unification if files are anonymous or originate from
different packages.
This is easier to do when all values in play are cue.Value.
The goal is to ultimately move away from cue.Instance
anwyay, so this is a first step in this simplification.
Fixes #743
Fixes #752
Change-Id: Ia0b3f367527eb97d32e665ba000d7bbbc14965ca
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8763
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go
index e8effa6..cbe713e 100644
--- a/cmd/cue/cmd/common.go
+++ b/cmd/cue/cmd/common.go
@@ -16,7 +16,6 @@
import (
"bytes"
- "fmt"
"io"
"os"
"path/filepath"
@@ -169,7 +168,7 @@
type iterator interface {
scan() bool
- instance() *cue.Instance
+ value() cue.Value
file() *ast.File // may return nil
err() error
close()
@@ -187,11 +186,11 @@
return i.i < len(i.a) && i.e == nil
}
-func (i *instanceIterator) close() {}
-func (i *instanceIterator) err() error { return i.e }
-func (i *instanceIterator) instance() *cue.Instance { return i.a[i.i] }
-func (i *instanceIterator) file() *ast.File { return nil }
-func (i *instanceIterator) id() string { return i.a[i.i].Dir }
+func (i *instanceIterator) close() {}
+func (i *instanceIterator) err() error { return i.e }
+func (i *instanceIterator) value() cue.Value { return i.a[i.i].Value() }
+func (i *instanceIterator) file() *ast.File { return nil }
+func (i *instanceIterator) id() string { return i.a[i.i].Dir }
type streamingIterator struct {
r *cue.Runtime
@@ -201,7 +200,7 @@
cfg *encoding.Config
a []*build.File
dec *encoding.Decoder
- i *cue.Instance
+ v cue.Value
f *ast.File
e error
}
@@ -240,8 +239,8 @@
return i
}
-func (i *streamingIterator) file() *ast.File { return i.f }
-func (i *streamingIterator) instance() *cue.Instance { return i.i }
+func (i *streamingIterator) file() *ast.File { return i.f }
+func (i *streamingIterator) value() cue.Value { return i.v }
func (i *streamingIterator) id() string {
if i.inst != nil {
@@ -284,15 +283,12 @@
i.e = err
return false
}
- i.i = inst
+ i.v = inst.Value()
if i.base.Exists() {
i.e = i.base.Err()
if i.e == nil {
- i.i, i.e = i.i.Fill(i.base)
- i.i.DisplayName = internal.DebugStr(i.b.schema)
- if inst.DisplayName != "" {
- i.i.DisplayName = fmt.Sprintf("%s|%s", inst.DisplayName, i.i.DisplayName)
- }
+ i.v = i.v.Unify(i.base)
+ i.e = i.v.Err()
}
i.f = nil
}
@@ -339,15 +335,11 @@
func (i *expressionIter) file() *ast.File { return nil }
-func (i *expressionIter) instance() *cue.Instance {
+func (i *expressionIter) value() cue.Value {
if len(i.expr) == 0 {
- return i.iter.instance()
+ return i.iter.value()
}
- inst := i.iter.instance()
- v := i.iter.instance().Eval(i.expr[i.i])
- ni := internal.MakeInstance(v).(*cue.Instance)
- ni.DisplayName = fmt.Sprintf("%s|%s", inst.DisplayName, i.expr[i.i])
- return ni
+ return internal.EvalExpr(i.iter.value(), i.expr[i.i]).(cue.Value)
}
type config struct {
diff --git a/cmd/cue/cmd/def.go b/cmd/cue/cmd/def.go
index bb21e4a..edba0e6 100644
--- a/cmd/cue/cmd/def.go
+++ b/cmd/cue/cmd/def.go
@@ -64,7 +64,7 @@
err := e.EncodeFile(f)
exitOnErr(cmd, err, true)
} else {
- err := e.Encode(iter.instance())
+ err := e.Encode(iter.value())
exitOnErr(cmd, err, true)
}
}
diff --git a/cmd/cue/cmd/eval.go b/cmd/cue/cmd/eval.go
index 0d90deb..8cb676a 100644
--- a/cmd/cue/cmd/eval.go
+++ b/cmd/cue/cmd/eval.go
@@ -117,7 +117,7 @@
if len(b.insts) > 1 {
id = iter.id()
}
- v := iter.instance().Value()
+ v := iter.value()
if flagConcrete.Bool(cmd) {
syn = append(syn, cue.Concrete(true))
diff --git a/cmd/cue/cmd/export.go b/cmd/cue/cmd/export.go
index 0f7669b..9a5e302 100644
--- a/cmd/cue/cmd/export.go
+++ b/cmd/cue/cmd/export.go
@@ -114,8 +114,8 @@
iter := b.instances()
defer iter.close()
for iter.scan() {
- inst := iter.instance()
- err = enc.Encode(inst)
+ v := iter.value()
+ err = enc.Encode(v)
exitOnErr(cmd, err, true)
}
exitOnErr(cmd, iter.err(), true)
diff --git a/cmd/cue/cmd/testdata/script/vet_embed.txt b/cmd/cue/cmd/testdata/script/vet_embed.txt
new file mode 100644
index 0000000..2ce8030
--- /dev/null
+++ b/cmd/cue/cmd/testdata/script/vet_embed.txt
@@ -0,0 +1,36 @@
+# Embedding at the file level should still close the package and validate
+# correctly against files from other packages without first merging these
+# files, thereby effectively embedding them and averting the closed check.
+
+# Issue #743
+
+! cue vet schema.cue foo.yaml
+cmp stderr expect-foo
+
+! cue vet schema.cue stream.yaml
+cmp stderr expect-stream
+
+-- schema.cue --
+package schema
+
+#Foo: {
+ a: int
+ b?: int
+}
+#Foo
+
+-- foo.yaml --
+a: 1
+c: 2
+
+-- stream.yaml --
+a: 1
+d: 2
+---
+a: 1
+e: 2
+
+-- expect-foo --
+field `c` not allowed
+-- expect-stream --
+field `d` not allowed
diff --git a/cmd/cue/cmd/vet.go b/cmd/cue/cmd/vet.go
index deb623d..b20a143 100644
--- a/cmd/cue/cmd/vet.go
+++ b/cmd/cue/cmd/vet.go
@@ -107,7 +107,7 @@
iter := b.instances()
defer iter.close()
for iter.scan() {
- inst := iter.instance()
+ v := iter.value()
// TODO: use ImportPath or some other sanitized path.
concrete := true
@@ -124,9 +124,9 @@
cue.Hidden(true),
}
w := cmd.Stderr()
- err := inst.Value().Validate(append(opt, cue.Concrete(concrete))...)
+ err := v.Validate(append(opt, cue.Concrete(concrete))...)
if err != nil && !hasFlag {
- err = inst.Value().Validate(append(opt, cue.Concrete(false))...)
+ err = v.Validate(append(opt, cue.Concrete(false))...)
if !shown && err == nil {
shown = true
p := message.NewPrinter(getLang())
@@ -150,7 +150,7 @@
iter := b.instances()
defer iter.close()
for iter.scan() {
- v := iter.instance().Value()
+ v := iter.value()
// Always concrete when checking against concrete files.
err := v.Validate(cue.Concrete(true))
diff --git a/internal/encoding/encoder.go b/internal/encoding/encoder.go
index f803d95..764d482 100644
--- a/internal/encoding/encoder.go
+++ b/internal/encoding/encoder.go
@@ -40,7 +40,7 @@
type Encoder struct {
cfg *Config
close func() error
- interpret func(*cue.Instance) (*ast.File, error)
+ interpret func(cue.Value) (*ast.File, error)
encFile func(*ast.File) error
encValue func(cue.Value) error
autoSimplify bool
@@ -79,7 +79,8 @@
case build.OpenAPI:
// TODO: get encoding options
cfg := &openapi.Config{}
- e.interpret = func(i *cue.Instance) (*ast.File, error) {
+ e.interpret = func(v cue.Value) (*ast.File, error) {
+ i := internal.MakeInstance(v).(*cue.Instance)
return openapi.Generate(i, cfg)
}
// case build.JSONSchema:
@@ -204,26 +205,25 @@
return e.encodeFile(f, e.interpret)
}
-func (e *Encoder) Encode(inst *cue.Instance) error {
+func (e *Encoder) Encode(v cue.Value) error {
e.autoSimplify = true
if e.interpret != nil {
- f, err := e.interpret(inst)
+ f, err := e.interpret(v)
if err != nil {
return err
}
return e.encodeFile(f, nil)
}
- v := inst.Value()
if err := v.Validate(cue.Concrete(e.concrete)); err != nil {
return err
}
if e.encValue != nil {
- return e.encValue(inst.Value())
+ return e.encValue(v)
}
- return e.encFile(valueToFile(inst.Value()))
+ return e.encFile(valueToFile(v))
}
-func (e *Encoder) encodeFile(f *ast.File, interpret func(*cue.Instance) (*ast.File, error)) error {
+func (e *Encoder) encodeFile(f *ast.File, interpret func(cue.Value) (*ast.File, error)) error {
if interpret == nil && e.encFile != nil {
return e.encFile(f)
}
@@ -234,13 +234,13 @@
return err
}
if interpret != nil {
- return e.Encode(inst)
+ return e.Encode(inst.Value())
}
v := inst.Value()
if err := v.Validate(cue.Concrete(e.concrete)); err != nil {
return err
}
- return e.encValue(inst.Value())
+ return e.encValue(v)
}
func writer(f *build.File, cfg *Config) (_ io.Writer, close func() error, err error) {