cmd/cue/cmd: add vet command
This is a simple vet command to avoid having to use eval
and printing oodles of output each time.
Also make validate obey RequireConcrete option.
This is relevant now eval allows non-concrete values by default
Change-Id: I52b4047014e308fdb708277f120d3a2c66db7768
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/1801
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go
index 5720d4e..4a03a17 100644
--- a/cmd/cue/cmd/common.go
+++ b/cmd/cue/cmd/common.go
@@ -15,7 +15,11 @@
package cmd
import (
+ "bytes"
"fmt"
+ "io"
+ "os"
+ "strings"
"cuelang.org/go/cue"
"cuelang.org/go/cue/build"
@@ -24,10 +28,31 @@
"github.com/spf13/cobra"
)
+func init() {
+ s, err := os.Getwd()
+ if err == nil {
+ cwd = s
+ }
+}
+
+var cwd = "////"
+
+// printHeader is a hacky and unprincipled way to sanatize the package path.
+func printHeader(w io.Writer, inst *cue.Instance) {
+ head := strings.Replace(inst.Dir, cwd, ".", 1)
+ fmt.Fprintf(w, "--- %s\n", head)
+}
+
func exitIfErr(cmd *cobra.Command, inst *cue.Instance, err error, fatal bool) {
if err != nil {
- fmt.Fprintf(cmd.OutOrStderr(), "--- %s\n", inst.Dir)
- errors.Print(cmd.OutOrStderr(), err)
+ w := &bytes.Buffer{}
+ printHeader(w, inst)
+ errors.Print(w, err)
+
+ // TODO: do something more principled than this.
+ b := w.Bytes()
+ b = bytes.ReplaceAll(b, []byte(cwd), []byte("."))
+ cmd.OutOrStderr().Write(b)
if fatal {
exit()
}
@@ -42,9 +67,15 @@
return buildInstances(cmd, binst)
}
+var (
+ config = &load.Config{
+ Context: build.NewContext(),
+ }
+)
+
func loadFromArgs(cmd *cobra.Command, args []string) []*build.Instance {
log.SetOutput(cmd.OutOrStderr())
- binst := load.Instances(args, nil)
+ binst := load.Instances(args, config)
if len(binst) == 0 {
return nil
}
diff --git a/cmd/cue/cmd/custom.go b/cmd/cue/cmd/custom.go
index fd7ead9..b31982d 100644
--- a/cmd/cue/cmd/custom.go
+++ b/cmd/cue/cmd/custom.go
@@ -71,7 +71,7 @@
// - parse flags and env vars
// - constrain current config with config section
- return doTasks(typ, name, tools)
+ return doTasks(cmd, typ, name, tools)
},
}
parent.AddCommand(sub)
@@ -110,9 +110,9 @@
return root.Lookup(k.typ, k.name, taskSection)
}
-func doTasks(typ, command string, root *cue.Instance) error {
+func doTasks(cmd *cobra.Command, typ, command string, root *cue.Instance) error {
if err := executeTasks(typ, command, root); err != nil {
- return fmt.Errorf("failed to run instance %q: %v", root.Dir, err)
+ exitIfErr(cmd, root, err, true)
}
return nil
}
@@ -121,7 +121,7 @@
//
// All tasks are started at once, but will block until tasks that they depend
// on will continue.
-func executeTasks(typ, command string, root *cue.Instance) error {
+func executeTasks(typ, command string, root *cue.Instance) (err error) {
spec := taskKey{typ, command, ""}
tasks := spec.lookupTasks(root)
diff --git a/cmd/cue/cmd/eval.go b/cmd/cue/cmd/eval.go
index 76a7b96..4f6d9f6 100644
--- a/cmd/cue/cmd/eval.go
+++ b/cmd/cue/cmd/eval.go
@@ -78,7 +78,13 @@
format.TabIndent(false),
}
if exprs == nil {
- format.Node(w, inst.Value().Syntax(syn...), opts...)
+ v := inst.Value()
+ if *compile {
+ err := v.Validate(cue.RequireConcrete())
+ exitIfErr(cmd, inst, err, false)
+ continue
+ }
+ format.Node(w, v.Syntax(syn...), opts...)
fmt.Fprintln(w)
}
for _, e := range exprs {
diff --git a/cmd/cue/cmd/export.go b/cmd/cue/cmd/export.go
index 26ff01c..5f651d0 100644
--- a/cmd/cue/cmd/export.go
+++ b/cmd/cue/cmd/export.go
@@ -96,6 +96,6 @@
func init() {
rootCmd.AddCommand(exportCmd)
- exportCmd.Flags().StringP("output", "o", "json", "output format (json only for now)")
+ // exportCmd.Flags().StringP("output", "o", "json", "output format (json only for now)")
escape = exportCmd.Flags().BoolP("escape", "e", false, "use HTML escaping")
}
diff --git a/cmd/cue/cmd/vet.go b/cmd/cue/cmd/vet.go
new file mode 100644
index 0000000..c2799f3
--- /dev/null
+++ b/cmd/cue/cmd/vet.go
@@ -0,0 +1,72 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cmd
+
+import (
+ "cuelang.org/go/cue"
+ "cuelang.org/go/cue/ast"
+ "cuelang.org/go/cue/parser"
+ "cuelang.org/go/cue/token"
+ "github.com/spf13/cobra"
+)
+
+// vetCmd represents the vet command
+var vetCmd = &cobra.Command{
+ Use: "vet",
+ Short: "A brief description of your command",
+ Long: `A longer description that spans multiple lines and likely contains examples
+and usage of using your command. For example:
+
+Cobra is a CLI library for Go that empowers applications.
+This application is a tool to generate the needed files
+to quickly create a Cobra application.`,
+ RunE: doVet,
+}
+
+func init() {
+ rootCmd.AddCommand(vetCmd)
+}
+
+func doVet(cmd *cobra.Command, args []string) error {
+
+ instances := buildFromArgs(cmd, args)
+
+ var exprs []ast.Expr
+ for _, e := range *expressions {
+ expr, err := parser.ParseExpr(token.NewFileSet(), "<expression flag>", e)
+ if err != nil {
+ return err
+ }
+ exprs = append(exprs, expr)
+ }
+
+ w := cmd.OutOrStdout()
+
+ for _, inst := range instances {
+ // TODO: use ImportPath or some other sanitized path.
+ opt := []cue.Option{
+ cue.Attributes(true),
+ cue.Optional(true),
+ cue.RequireConcrete(),
+ cue.Hidden(true),
+ }
+ err := inst.Value().Validate(opt...)
+ if *fVerbose || err != nil {
+ printHeader(w, inst)
+ }
+ exitIfErr(cmd, inst, err, false)
+ }
+ return nil
+}
diff --git a/cue/types.go b/cue/types.go
index 35a9576..5184904 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -1077,12 +1077,35 @@
return false // mostly to avoid some hypothetical cycle issue
}
}
+ if o.concrete {
+ if err := isGroundRecursive(v.ctx(), v.eval(v.ctx())); err != nil {
+ list.Add(err)
+ }
+ }
return true
}, nil)
if len(list) > 0 {
list.Sort()
// list.RemoveMultiples() // TODO: use RemoveMultiples when it is fixed
- return list
+ // return list
+ return list[0]
+ }
+ return nil
+}
+
+func isGroundRecursive(ctx *context, v value) error {
+ switch x := v.(type) {
+ case *list:
+ for i := 0; i < len(x.a); i++ {
+ v := ctx.manifest(x.at(ctx, i))
+ if err := isGroundRecursive(ctx, v); err != nil {
+ return err
+ }
+ }
+ default:
+ if !x.kind().isGround() {
+ return ctx.mkErr(v, "incomplete value %q", debugStr(ctx, v))
+ }
}
return nil
}