diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go index bf24aceb2..13a5afd12 100644 --- a/cmd/cue/cmd/common.go +++ b/cmd/cue/cmd/common.go @@ -123,6 +123,10 @@ type buildPlan struct { cmd *Command insts []*build.Instance + // instance is a pre-compiled instance, which exists if value files are + // being processed, which may require a schema to decode. + instance *cue.Instance + cfg *config // If orphanFiles are mixed with CUE files and/or if placement flags are used, @@ -149,7 +153,6 @@ type buildPlan struct { outFile *build.File encConfig *encoding.Config - merge []*build.Instance } // instances iterates either over a list of instances, or a list of @@ -157,10 +160,18 @@ type buildPlan struct { // instance, with which the data instance may be merged. func (b *buildPlan) instances() iterator { var i iterator - if len(b.orphaned) == 0 { - i = &instanceIterator{a: buildInstances(b.cmd, b.insts), i: -1} - } else { + switch { + case len(b.orphaned) > 0: i = newStreamingIterator(b) + case len(b.insts) > 0: + i = &instanceIterator{ + inst: b.instance, + a: buildInstances(b.cmd, b.insts), + i: -1, + } + default: + i = &instanceIterator{a: []*cue.Instance{b.instance}, i: -1} + b.instance = nil } if len(b.expressions) > 0 { return &expressionIter{ @@ -183,9 +194,10 @@ type iterator interface { } type instanceIterator struct { - a []*cue.Instance - i int - e error + inst *cue.Instance + a []*cue.Instance + i int + e error } func (i *instanceIterator) scan() bool { @@ -193,12 +205,23 @@ func (i *instanceIterator) scan() bool { return i.i < len(i.a) && i.e == nil } -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) 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 { + v := i.a[i.i].Value() + if i.inst != nil { + v = v.Unify(i.inst.Value()) + } + return v +} +func (i *instanceIterator) instance() *cue.Instance { + if i.inst != nil { + return nil + } + return i.a[i.i] +} +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 @@ -223,13 +246,16 @@ func newStreamingIterator(b *buildPlan) *streamingIterator { switch len(b.insts) { case 0: i.r = &cue.Runtime{} + if v := b.encConfig.Schema; v.Exists() { + i.r = internal.GetRuntime(v).(*cue.Runtime) + } case 1: p := b.insts[0] inst := buildInstances(b.cmd, []*build.Instance{p})[0] if inst.Err != nil { return &streamingIterator{e: inst.Err} } - i.r = internal.GetRuntime(inst).(*cue.Runtime) + b.instance = inst if b.schema == nil { b.encConfig.Schema = inst.Value() } else { @@ -495,10 +521,6 @@ func parseArgs(cmd *Command, args []string, cfg *config) (p *buildPlan, err erro return nil, err } - // TODO(v0.4.0): what to do: - // - when there is already a single instance - // - when we are importing and there are schemas and values. - if values == nil { values, schemas = schemas, values } @@ -515,42 +537,64 @@ func parseArgs(cmd *Command, args []string, cfg *config) (p *buildPlan, err erro } } - // TODO(v0.4.0): if schema is specified and data format is JSON, YAML or - // Protobuf, reinterpret with schema. + if len(p.insts) > 1 && p.schema != nil { + return nil, errors.Newf(token.NoPos, + "cannot use --schema/-d with flag more than one schema") + } - switch { - case p.usePlacement() || p.importing: - if err = p.placeOrphans(b, values); err != nil { - return nil, err + var schema *build.Instance + switch n := len(p.insts); n { + default: + return nil, errors.Newf(token.NoPos, + "too many packages defined (%d) in combination with files", n) + case 1: + if len(schemas) > 0 { + return nil, errors.Newf(token.NoPos, + "cannot combine packages with individual schema files") } + schema = p.insts[0] + p.insts = nil + + case 0: + bb := *b + schema = &bb + b.BuildFiles = nil + b.Files = nil + } - case !p.mergeData || p.schema != nil: - p.orphaned = values + if schema != nil && len(schema.Files) > 0 { + inst := buildInstances(p.cmd, []*build.Instance{schema})[0] - default: - for _, di := range values { - d := di.dec - for ; !d.Done(); d.Next() { - if err := b.AddSyntax(d.File()); err != nil { - return nil, err - } - } - if err := d.Err(); err != nil { + if inst.Err != nil { + return nil, err + } + p.instance = inst + p.encConfig.Schema = inst.Value() + if p.schema != nil { + v := inst.Eval(p.schema) + if err := v.Err(); err != nil { return nil, err } + p.encConfig.Schema = v } } + switch { + case p.mergeData, p.usePlacement(), p.importing: + // case p.usePlacement() || p.importing: + if err = p.placeOrphans(b, values); err != nil { + return nil, err + } + + default: + p.orphaned = values + } + if len(b.Files) > 0 { p.insts = append(p.insts, b) } } - if len(p.insts) > 1 && p.schema != nil { - return nil, errors.Newf(token.NoPos, - "cannot use --schema/-d flag more than one schema") - } - if len(p.expressions) > 1 { p.encConfig.Stream = true } diff --git a/cmd/cue/cmd/vet.go b/cmd/cue/cmd/vet.go index ac8843613..61baf280a 100644 --- a/cmd/cue/cmd/vet.go +++ b/cmd/cue/cmd/vet.go @@ -143,7 +143,7 @@ func doVet(cmd *Command, args []string) error { func vetFiles(cmd *Command, b *buildPlan) { // Use -r type root, instead of -e - if len(b.insts) == 0 { + if !b.encConfig.Schema.Exists() { exitOnErr(cmd, errors.New("data files specified without a schema"), true) }