cmd/cue/cmd: add def command

Closes #257
Closes #91

Change-Id: Ia91d7ddd018dc9ca2c142cf59445205e46da7f79
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5096
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/def.go b/cmd/cue/cmd/def.go
new file mode 100644
index 0000000..c997260
--- /dev/null
+++ b/cmd/cue/cmd/def.go
@@ -0,0 +1,83 @@
+// 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 (
+	"github.com/spf13/cobra"
+
+	"cuelang.org/go/internal/encoding"
+	"cuelang.org/go/internal/filetypes"
+)
+
+// newDefCmd creates a new eval command
+func newDefCmd(c *Command) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "def",
+		Short: "print consolidated definitions",
+		Long: `def prints consolidated configuration as a single file.
+
+Printing is skipped if validation fails.
+
+The --expression flag is used to only print parts of a configuration.
+`,
+		RunE: mkRunE(c, runDef),
+	}
+
+	flagOut.Add(cmd)
+
+	addOrphanFlags(cmd.Flags())
+
+	cmd.Flags().StringArrayP(string(flagExpression), "e", nil, "evaluate this expression only")
+
+	cmd.Flags().BoolP(string(flagAttributes), "A", false,
+		"display field attributes")
+
+	cmd.Flags().StringArrayP(string(flagTags), "t", nil,
+		"set the value of a tagged field")
+
+	// TODO: Option to include comments in output.
+	return cmd
+}
+
+func runDef(cmd *Command, args []string) error {
+	b, err := parseArgs(cmd, args, nil)
+	exitOnErr(cmd, err, true)
+
+	b.encConfig.Mode = filetypes.Def
+
+	out := flagOut.String(cmd)
+	if out == "" {
+		out = "-"
+	}
+	f, err := filetypes.ParseFile(out, filetypes.Def)
+	exitOnErr(cmd, err, true)
+
+	e, err := encoding.NewEncoder(f, b.encConfig)
+	exitOnErr(cmd, err, true)
+
+	iter := b.instances()
+	defer iter.close()
+	for i := 0; iter.scan(); i++ {
+		if f := iter.file(); f != nil {
+			err := e.EncodeFile(f)
+			exitOnErr(cmd, err, true)
+		} else {
+			err := e.Encode(iter.instance())
+			exitOnErr(cmd, err, true)
+		}
+	}
+	exitOnErr(cmd, iter.err(), true)
+	return nil
+}
diff --git a/cmd/cue/cmd/root.go b/cmd/cue/cmd/root.go
index 83a4084..b9396ae 100644
--- a/cmd/cue/cmd/root.go
+++ b/cmd/cue/cmd/root.go
@@ -90,6 +90,7 @@
 	subCommands := []*cobra.Command{
 		cmdCmd,
 		newEvalCmd(c),
+		newDefCmd(c),
 		newExportCmd(c),
 		newFmtCmd(c),
 		newGetCmd(c),
diff --git a/cmd/cue/cmd/testdata/script/def_basic.txt b/cmd/cue/cmd/testdata/script/def_basic.txt
new file mode 100644
index 0000000..f375c2a
--- /dev/null
+++ b/cmd/cue/cmd/testdata/script/def_basic.txt
@@ -0,0 +1,23 @@
+cue def
+cmp stdout expect-stdout
+
+-- schema.cue --
+// foo
+package foo
+
+A :: {
+    // a is an integer
+    a: int
+    b: int
+}
+
+-- expect-stdout --
+
+// foo
+package foo
+
+A :: {
+    // a is an integer
+    a: int
+    b: int
+}