cmd/cue/cmd: factor out common flag code

This changes the use of the -e flag for vet to use -d.

The -e flag means something different for other
commands and we may also want to include this in
vet, ultimately.

Also removed unused flags and made some global
flags non-global.

Change-Id: I95771f14705733734d547de6275e190a28cb1740
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5025
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/add.go b/cmd/cue/cmd/add.go
index b4dd65c..5af96d5 100644
--- a/cmd/cue/cmd/add.go
+++ b/cmd/cue/cmd/add.go
@@ -53,6 +53,7 @@
 		"text executed as Go template with instance info")
 	f.BoolP(string(flagDryrun), "n", false,
 		"only run simulation")
+	f.StringP(string(flagPackage), "p", "", "package to append to")
 
 	return cmd
 }
diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go
index 8d52bde..976f9a0 100644
--- a/cmd/cue/cmd/common.go
+++ b/cmd/cue/cmd/common.go
@@ -32,6 +32,7 @@
 	"cuelang.org/go/cue/load"
 	"cuelang.org/go/cue/parser"
 	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal/encoding"
 )
 
 // Disallow
@@ -100,15 +101,6 @@
 	}
 }
 
-func buildFromArgs(cmd *Command, args []string) []*cue.Instance {
-	binst := loadFromArgs(cmd, args, defaultConfig)
-	if binst == nil {
-		return nil
-	}
-	decorateInstances(cmd, flagTags.StringArray(cmd), binst)
-	return buildInstances(cmd, binst)
-}
-
 func loadFromArgs(cmd *Command, args []string, cfg *load.Config) []*build.Instance {
 	binst := load.Instances(args, cfg)
 	if len(binst) == 0 {
@@ -132,7 +124,11 @@
 	orphanedSchema []*build.File
 	orphanInstance *build.Instance
 
-	merge []*build.Instance
+	expressions []ast.Expr // only evaluate these expressions within results
+	schema      ast.Expr   // selects schema in instance for orphaned values
+
+	encConfig *encoding.Config
+	merge     []*build.Instance
 }
 
 func (b *buildPlan) instances() []*cue.Instance {
@@ -152,12 +148,12 @@
 	}
 	decorateInstances(cmd, flagTags.StringArray(cmd), builds)
 
-	return splitBuilds(cmd, builds)
-}
-
-func splitBuilds(cmd *Command, builds []*build.Instance) (*buildPlan, errors.Error) {
 	p := &buildPlan{cmd: cmd}
 
+	if err := p.parseFlags(); err != nil {
+		return nil, err
+	}
+
 	for _, b := range builds {
 		if !b.User {
 			p.insts = append(p.insts, b)
@@ -197,6 +193,28 @@
 	return p, nil
 }
 
+func (b *buildPlan) parseFlags() (err error) {
+	for _, e := range flagExpression.StringArray(b.cmd) {
+		expr, err := parser.ParseExpr("--expression", e)
+		if err != nil {
+			return err
+		}
+		b.expressions = append(b.expressions, expr)
+	}
+	if s := flagSchema.String(b.cmd); s != "" {
+		b.schema, err = parser.ParseExpr("--schema", s)
+		if err != nil {
+			return err
+		}
+	}
+	b.encConfig = &encoding.Config{
+		Stdin:     stdin,
+		Stdout:    b.cmd.OutOrStdout(),
+		ProtoPath: flagProtoPath.StringArray(b.cmd),
+	}
+	return nil
+}
+
 func (b *buildPlan) singleInstance() *cue.Instance {
 	var p *build.Instance
 	switch len(b.insts) {
diff --git a/cmd/cue/cmd/eval.go b/cmd/cue/cmd/eval.go
index 3781d01..0508d4e 100644
--- a/cmd/cue/cmd/eval.go
+++ b/cmd/cue/cmd/eval.go
@@ -23,7 +23,6 @@
 	"cuelang.org/go/cue"
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/format"
-	"cuelang.org/go/cue/parser"
 )
 
 // newEvalCmd creates a new eval command
@@ -86,15 +85,6 @@
 	b, err := parseArgs(cmd, args, nil)
 	exitOnErr(cmd, err, false)
 
-	var exprs []ast.Expr
-	for _, e := range flagExpression.StringArray(cmd) {
-		expr, err := parser.ParseExpr("<expression flag>", e)
-		if err != nil {
-			return err
-		}
-		exprs = append(exprs, expr)
-	}
-
 	w := cmd.OutOrStdout()
 	// Always print a trailing newline. format.Node may not write a trailing
 	// newline if the output is single-line expression.
@@ -131,7 +121,7 @@
 			opts = append(opts, format.Simplify())
 		}
 
-		if exprs == nil {
+		if b.expressions == nil {
 			v := inst.Value()
 			if flagConcrete.Bool(cmd) && !flagIgnore.Bool(cmd) {
 				if err := v.Validate(cue.Concrete(true)); err != nil {
@@ -141,8 +131,8 @@
 			}
 			writeNode(format.Node(getSyntax(v, syn), opts...))
 		}
-		for _, e := range exprs {
-			if len(exprs) > 1 {
+		for _, e := range b.expressions {
+			if len(b.expressions) > 1 {
 				fmt.Fprint(w, "// ")
 				writeNode(format.Node(e))
 			}
diff --git a/cmd/cue/cmd/export.go b/cmd/cue/cmd/export.go
index 63e6ccd..ab29fd1 100644
--- a/cmd/cue/cmd/export.go
+++ b/cmd/cue/cmd/export.go
@@ -17,8 +17,6 @@
 import (
 	"github.com/spf13/cobra"
 
-	"cuelang.org/go/cue/ast"
-	"cuelang.org/go/cue/parser"
 	"cuelang.org/go/internal/encoding"
 	"cuelang.org/go/internal/filetypes"
 )
@@ -102,36 +100,22 @@
 func runExport(cmd *Command, args []string) error {
 	b, err := parseArgs(cmd, args, nil)
 	exitOnErr(cmd, err, true)
-	w := cmd.OutOrStdout()
-
-	var exprs []ast.Expr
-	for _, e := range flagExpression.StringArray(cmd) {
-		expr, err := parser.ParseExpr("<expression flag>", e)
-		if err != nil {
-			return err
-		}
-		exprs = append(exprs, expr)
-	}
 
 	format := flagMedia.String(cmd) + ":-"
 	f, err := filetypes.ParseFile(format, filetypes.Export)
 	exitOnErr(cmd, err, true)
 
-	cfg := &encoding.Config{
-		Out: w,
-	}
-
-	enc, err := encoding.NewEncoder(f, cfg)
+	enc, err := encoding.NewEncoder(f, b.encConfig)
 	exitOnErr(cmd, err, true)
 	defer enc.Close()
 
 	for _, inst := range b.instances() {
-		if exprs == nil {
+		if b.expressions == nil {
 			err = enc.Encode(inst.Value())
 			exitOnErr(cmd, err, true)
 			continue
 		}
-		for _, e := range exprs {
+		for _, e := range b.expressions {
 			v := inst.Eval(e)
 			exitOnErr(cmd, v.Err(), true)
 			err = enc.Encode(v)
diff --git a/cmd/cue/cmd/flags.go b/cmd/cue/cmd/flags.go
index 92ad727..cf7f7cf 100644
--- a/cmd/cue/cmd/flags.go
+++ b/cmd/cue/cmd/flags.go
@@ -29,10 +29,10 @@
 	flagIgnore   flagName = "ignore"
 	flagSimplify flagName = "simplify"
 	flagPackage  flagName = "package"
-	flagDebug    flagName = "debug"
 	flagTags     flagName = "tags"
 
 	flagExpression flagName = "expression"
+	flagSchema     flagName = "schema"
 	flagEscape     flagName = "escape"
 	flagGlob       flagName = "name"
 	flagRecursive  flagName = "recursive"
@@ -54,12 +54,8 @@
 }
 
 func addGlobalFlags(f *pflag.FlagSet) {
-	f.Bool(string(flagDebug), false,
-		"give detailed error info")
 	f.Bool(string(flagTrace), false,
 		"trace computation")
-	f.StringP(string(flagPackage), "p", "",
-		"CUE package to evaluate")
 	f.BoolP(string(flagSimplify), "s", false,
 		"simplify output")
 	f.BoolP(string(flagIgnore), "i", false,
@@ -68,6 +64,16 @@
 		"print information about progress")
 }
 
+func addOrphanFlags(f *pflag.FlagSet) {
+	f.StringP(string(flagPackage), "p", "", "package name for non-CUE files")
+	f.StringArrayP(string(flagPath), "l", nil, "CUE expression for single path component")
+	f.Bool(string(flagList), false, "concatenate multiple objects into a list")
+	f.Bool(string(flagFiles), false, "split multiple entries into different files")
+	f.Bool(string(flagWithContext), false, "import as object with contextual data")
+	f.StringArrayP(string(flagProtoPath), "I", nil, "paths in which to search for imports")
+	f.StringP(string(flagGlob), "n", "", "glob filter for file names")
+}
+
 type flagName string
 
 func (f flagName) Bool(cmd *Command) bool {
diff --git a/cmd/cue/cmd/import.go b/cmd/cue/cmd/import.go
index 0b8cab5..015e43d 100644
--- a/cmd/cue/cmd/import.go
+++ b/cmd/cue/cmd/import.go
@@ -224,21 +224,15 @@
 	}
 
 	flagOut.Add(cmd)
-	cmd.Flags().StringP(string(flagGlob), "n", "", "glob filter for file names")
+
+	addOrphanFlags(cmd.Flags())
+
 	cmd.Flags().String(string(flagType), "", "only apply to files of this type")
 	cmd.Flags().BoolP(string(flagForce), "f", false, "force overwriting existing files")
 	cmd.Flags().Bool(string(flagDryrun), false, "only run simulation")
-
-	cmd.Flags().StringArrayP(string(flagPath), "l", nil, "CUE expression for single path component")
-	cmd.Flags().Bool(string(flagList), false, "concatenate multiple objects into a list")
-	cmd.Flags().Bool(string(flagFiles), false, "split multiple entries into different files")
 	cmd.Flags().BoolP(string(flagRecursive), "R", false, "recursively parse string values")
-	cmd.Flags().Bool(string(flagWithContext), false, "import as object with contextual data")
-
 	cmd.Flags().String("fix", "", "apply given fix")
 
-	cmd.Flags().StringArrayP(string(flagProtoPath), "I", nil, "paths in which to search for imports")
-
 	return cmd
 }
 
@@ -285,7 +279,7 @@
 
 		for _, f := range pkg.OrphanedFiles {
 			f := f // capture range var
-			group.Go(func() error { return handleFile(cmd, pkgName, f) })
+			group.Go(func() error { return handleFile(b, pkgName, f) })
 		}
 	}
 
@@ -295,10 +289,10 @@
 	return nil
 }
 
-func handleFile(cmd *Command, pkg string, file *build.File) (err error) {
+func handleFile(b *buildPlan, pkg string, file *build.File) (err error) {
 	filename := file.Filename
 	// filter file names
-	re, err := regexp.Compile(flagGlob.String(cmd))
+	re, err := regexp.Compile(flagGlob.String(b.cmd))
 	if err != nil {
 		return err
 	}
@@ -308,24 +302,20 @@
 
 	// TODO: consider unifying the two modes.
 	var objs []ast.Expr
-	i := encoding.NewDecoder(file, &encoding.Config{
-		Stdin:     stdin,
-		Stdout:    stdout,
-		ProtoPath: flagProtoPath.StringArray(cmd),
-	})
+	i := encoding.NewDecoder(file, b.encConfig)
 	defer i.Close()
 	for ; !i.Done(); i.Next() {
 		if expr := i.Expr(); expr != nil {
 			objs = append(objs, expr)
 			continue
 		}
-		if err := processFile(cmd, i.File()); err != nil {
+		if err := processFile(b.cmd, i.File()); err != nil {
 			return err
 		}
 	}
 
 	if len(objs) > 0 {
-		if err := processStream(cmd, pkg, filename, objs); err != nil {
+		if err := processStream(b.cmd, pkg, filename, objs); err != nil {
 			return err
 		}
 	}
diff --git a/cmd/cue/cmd/testdata/script/eval_e.txt b/cmd/cue/cmd/testdata/script/eval_e.txt
index 18c79b0..7723916 100644
--- a/cmd/cue/cmd/testdata/script/eval_e.txt
+++ b/cmd/cue/cmd/testdata/script/eval_e.txt
@@ -5,7 +5,7 @@
 -- expect-stdout --
 -- expect-stderr --
 reference "nonExist" not found:
-    <expression flag>:1:1
+    --expression:1:1
 -- partial.cue --
 package exitcode
 
diff --git a/cmd/cue/cmd/testdata/script/help_cmd.txt b/cmd/cue/cmd/testdata/script/help_cmd.txt
index 4da924c..c900461 100644
--- a/cmd/cue/cmd/testdata/script/help_cmd.txt
+++ b/cmd/cue/cmd/testdata/script/help_cmd.txt
@@ -225,11 +225,9 @@
   -t, --tags stringArray   set the value of a tagged field
 
 Global Flags:
-      --debug            give detailed error info
-  -i, --ignore           proceed in the presence of errors
-  -p, --package string   CUE package to evaluate
-  -s, --simplify         simplify output
-      --trace            trace computation
-  -v, --verbose          print information about progress
+  -i, --ignore     proceed in the presence of errors
+  -s, --simplify   simplify output
+      --trace      trace computation
+  -v, --verbose    print information about progress
 
 Use "cue cmd [command] --help" for more information about a command.
diff --git a/cmd/cue/cmd/testdata/script/help_hello.txt b/cmd/cue/cmd/testdata/script/help_hello.txt
index f8b38ba..4a0a093 100644
--- a/cmd/cue/cmd/testdata/script/help_hello.txt
+++ b/cmd/cue/cmd/testdata/script/help_hello.txt
@@ -29,9 +29,7 @@
   -h, --help   help for hello
 
 Global Flags:
-      --debug            give detailed error info
-  -i, --ignore           proceed in the presence of errors
-  -p, --package string   CUE package to evaluate
-  -s, --simplify         simplify output
-      --trace            trace computation
-  -v, --verbose          print information about progress
+  -i, --ignore     proceed in the presence of errors
+  -s, --simplify   simplify output
+      --trace      trace computation
+  -v, --verbose    print information about progress
diff --git a/cmd/cue/cmd/testdata/script/vet_expr.txt b/cmd/cue/cmd/testdata/script/vet_expr.txt
index 31fbfb4..0a79f46 100644
--- a/cmd/cue/cmd/testdata/script/vet_expr.txt
+++ b/cmd/cue/cmd/testdata/script/vet_expr.txt
@@ -1,4 +1,4 @@
-! cue vet -e File vet.cue data.yaml
+! cue vet -d File vet.cue data.yaml
 cmp stderr expect-stderr
 
 -- expect-stderr --
diff --git a/cmd/cue/cmd/vet.go b/cmd/cue/cmd/vet.go
index a4960b7..f0ea5e2 100644
--- a/cmd/cue/cmd/vet.go
+++ b/cmd/cue/cmd/vet.go
@@ -20,7 +20,6 @@
 
 	"cuelang.org/go/cue"
 	"cuelang.org/go/cue/errors"
-	"cuelang.org/go/cue/parser"
 	"cuelang.org/go/internal"
 	"cuelang.org/go/internal/encoding"
 )
@@ -75,9 +74,8 @@
 	cmd.Flags().BoolP(string(flagConcrete), "c", false,
 		"require the evaluation to be concrete")
 
-	// TODO: change to -d as -e means something different here as then in eval.
-	cmd.Flags().StringArrayP(string(flagExpression), "e", nil,
-		"use this expression to validate non-CUE files")
+	cmd.Flags().StringP(string(flagSchema), "d", "",
+		"expression to select schema for evaluating values in non-CUE files")
 
 	cmd.Flags().StringArrayP(string(flagTags), "t", nil,
 		"set the value of a tagged field")
@@ -141,7 +139,7 @@
 
 func vetFiles(cmd *Command, b *buildPlan) {
 	// Use -r type root, instead of -e
-	expressions := flagExpression.StringArray(cmd)
+	expr := flagSchema.String(cmd)
 
 	var check cue.Value
 
@@ -150,27 +148,17 @@
 		exitOnErr(cmd, errors.New("data files specified without a schema"), true)
 	}
 
-	if len(expressions) == 0 {
+	if expr == "" {
 		check = inst.Value()
-	}
-
-	for _, e := range expressions {
-		expr, err := parser.ParseExpr("<expression flag>", e)
-		exitIfErr(cmd, inst, err, true)
-
-		v := inst.Eval(expr)
-		exitIfErr(cmd, inst, v.Err(), true)
-		check = check.Unify(v)
+	} else {
+		check = inst.Eval(b.schema)
+		exitIfErr(cmd, inst, check.Err(), true)
 	}
 
 	r := internal.GetRuntime(inst).(*cue.Runtime)
 
 	for _, f := range b.orphanedData {
-		i := encoding.NewDecoder(f, &encoding.Config{
-			Stdin:     stdin,
-			Stdout:    stdout,
-			ProtoPath: flagProtoPath.StringArray(cmd),
-		})
+		i := encoding.NewDecoder(f, b.encConfig)
 		defer i.Close()
 		for ; !i.Done(); i.Next() {
 			body, err := r.CompileExpr(i.Expr())