cmd/cue/cmd: harmonize output flags

Issue #280

Change-Id: I36de4b3ec70b802ecfe6264015954930dc4bdc16
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5243
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go
index 81d9550..dc49c6f 100644
--- a/cmd/cue/cmd/common.go
+++ b/cmd/cue/cmd/common.go
@@ -36,6 +36,7 @@
 	"cuelang.org/go/cue/token"
 	"cuelang.org/go/internal"
 	"cuelang.org/go/internal/encoding"
+	"cuelang.org/go/internal/filetypes"
 )
 
 // Disallow
@@ -412,6 +413,23 @@
 	return p, nil
 }
 
+func (b *buildPlan) out(def string, mode filetypes.Mode) (*build.File, error) {
+	out := flagOut.String(b.cmd)
+	outFile := flagOutFile.String(b.cmd)
+
+	if strings.Contains(out, ":") && strings.Contains(outFile, ":") {
+		return nil, errors.Newf(token.NoPos,
+			"cannot specify qualifier in both --out and --outfile")
+	}
+	if outFile == "" {
+		outFile = def
+	}
+	if out != "" {
+		outFile = out + ":" + outFile
+	}
+	return filetypes.ParseFile(outFile, mode)
+}
+
 func (b *buildPlan) parseFlags() (err error) {
 	for _, e := range flagExpression.StringArray(b.cmd) {
 		expr, err := parser.ParseExpr("--expression", e)
diff --git a/cmd/cue/cmd/def.go b/cmd/cue/cmd/def.go
index c997260..85cea1c 100644
--- a/cmd/cue/cmd/def.go
+++ b/cmd/cue/cmd/def.go
@@ -35,8 +35,7 @@
 		RunE: mkRunE(c, runDef),
 	}
 
-	flagOut.Add(cmd)
-
+	addOutFlags(cmd.Flags(), true)
 	addOrphanFlags(cmd.Flags())
 
 	cmd.Flags().StringArrayP(string(flagExpression), "e", nil, "evaluate this expression only")
@@ -57,11 +56,7 @@
 
 	b.encConfig.Mode = filetypes.Def
 
-	out := flagOut.String(cmd)
-	if out == "" {
-		out = "-"
-	}
-	f, err := filetypes.ParseFile(out, filetypes.Def)
+	f, err := b.out("-", filetypes.Def)
 	exitOnErr(cmd, err, true)
 
 	e, err := encoding.NewEncoder(f, b.encConfig)
diff --git a/cmd/cue/cmd/eval.go b/cmd/cue/cmd/eval.go
index c2f47f4..ee9e839 100644
--- a/cmd/cue/cmd/eval.go
+++ b/cmd/cue/cmd/eval.go
@@ -50,6 +50,7 @@
 		RunE: mkRunE(c, runEval),
 	}
 
+	addOutFlags(cmd.Flags(), true)
 	addOrphanFlags(cmd.Flags())
 
 	cmd.Flags().StringArrayP(string(flagExpression), "e", nil, "evaluate this expression only")
diff --git a/cmd/cue/cmd/export.go b/cmd/cue/cmd/export.go
index c7d6f9c..b05b577 100644
--- a/cmd/cue/cmd/export.go
+++ b/cmd/cue/cmd/export.go
@@ -90,9 +90,9 @@
 		RunE: mkRunE(c, runExport),
 	}
 
+	addOutFlags(cmd.Flags(), true)
 	addOrphanFlags(cmd.Flags())
 
-	flagMedia.Add(cmd)
 	cmd.Flags().Bool(string(flagEscape), false, "use HTML escaping")
 
 	cmd.Flags().StringArrayP(string(flagExpression), "e", nil, "export this expression only")
@@ -107,8 +107,7 @@
 	b, err := parseArgs(cmd, args, nil)
 	exitOnErr(cmd, err, true)
 
-	format := flagMedia.String(cmd) + ":-"
-	f, err := filetypes.ParseFile(format, filetypes.Export)
+	f, err := b.out("-", filetypes.Export)
 	exitOnErr(cmd, err, true)
 
 	enc, err := encoding.NewEncoder(f, b.encConfig)
diff --git a/cmd/cue/cmd/flags.go b/cmd/cue/cmd/flags.go
index cf7f7cf..8d721a1 100644
--- a/cmd/cue/cmd/flags.go
+++ b/cmd/cue/cmd/flags.go
@@ -39,18 +39,17 @@
 	flagType       flagName = "type"
 	flagList       flagName = "list"
 	flagPath       flagName = "path"
+	flagOut        flagName = "out"
+	flagOutFile    flagName = "outfile"
 )
 
-var flagMedia = stringFlag{
-	name: "out",
-	text: "output format (json, yaml or text)",
-	def:  "json",
-}
-
-var flagOut = stringFlag{
-	name:  "out",
-	short: "o",
-	text:  "alternative output or - for stdout",
+func addOutFlags(f *pflag.FlagSet, allowNonCUE bool) {
+	if allowNonCUE {
+		f.String(string(flagOut), "",
+			`output format (run 'cue filetype' for more info)`)
+	}
+	f.StringP(string(flagOutFile), "o", "",
+		`filename or - for stdout with optional file prefix (run 'cue filetype' for more info)`)
 }
 
 func addGlobalFlags(f *pflag.FlagSet) {
diff --git a/cmd/cue/cmd/import.go b/cmd/cue/cmd/import.go
index 36e1381..52ca926 100644
--- a/cmd/cue/cmd/import.go
+++ b/cmd/cue/cmd/import.go
@@ -213,7 +213,7 @@
 		RunE: mkRunE(c, runImport),
 	}
 
-	flagOut.Add(cmd)
+	addOutFlags(cmd.Flags(), false)
 
 	addOrphanFlags(cmd.Flags())
 
@@ -266,7 +266,7 @@
 
 func handleFile(b *buildPlan, f *ast.File) (err error) {
 	cueFile := f.Filename
-	if out := flagOut.String(b.cmd); out != "" {
+	if out := flagOutFile.String(b.cmd); out != "" {
 		cueFile = out
 	}
 	if cueFile != "-" {
diff --git a/cmd/cue/cmd/trim.go b/cmd/cue/cmd/trim.go
index 3df6902..a180b09 100644
--- a/cmd/cue/cmd/trim.go
+++ b/cmd/cue/cmd/trim.go
@@ -88,7 +88,8 @@
 		RunE: mkRunE(c, runTrim),
 	}
 
-	flagOut.Add(cmd)
+	addOutFlags(cmd.Flags(), false)
+
 	return cmd
 }
 
@@ -109,8 +110,7 @@
 	}
 	instances := buildInstances(cmd, binst)
 
-	// dst := flagName("o").String(cmd)
-	dst := flagOut.String(cmd)
+	dst := flagOutFile.String(cmd)
 	if dst != "" && dst != "-" {
 		switch _, err := os.Stat(dst); {
 		case os.IsNotExist(err):