cmd/cue/cmd: move vet to filetypes

Also change package loading. This now allows packages
and files arguments. File arguments must follow the
packages.

Closes #183

Change-Id: Ie88f486f50e10de4ce029c54bc4c6f8c12bae25e
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5020
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/encoding/encoding.go b/internal/encoding/encoding.go
new file mode 100644
index 0000000..a032bde
--- /dev/null
+++ b/internal/encoding/encoding.go
@@ -0,0 +1,152 @@
+// Copyright 2020 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 encoding
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/build"
+	"cuelang.org/go/encoding/json"
+	"cuelang.org/go/encoding/protobuf"
+	"cuelang.org/go/internal/third_party/yaml"
+)
+
+type Decoder struct {
+	closer   io.Closer
+	next     func() (ast.Expr, error)
+	expr     ast.Expr
+	file     *ast.File
+	filename string // may change on iteration for some formats
+	index    int
+	err      error
+}
+
+func (i *Decoder) Expr() ast.Expr   { return i.expr }
+func (i *Decoder) Filename() string { return i.filename }
+func (i *Decoder) Index() int       { return i.index }
+func (i *Decoder) Done() bool       { return i.err != nil }
+
+func (i *Decoder) Next() {
+	if i.err == nil {
+		i.expr, i.err = i.next()
+		i.index++
+	}
+}
+
+func (i *Decoder) File() *ast.File {
+	if i.file != nil {
+		return i.file
+	}
+	switch x := i.expr.(type) {
+	case nil:
+		return nil
+	case *ast.StructLit:
+		return &ast.File{Decls: x.Elts}
+	default:
+		return &ast.File{
+			Decls: []ast.Decl{&ast.EmbedDecl{Expr: i.expr}},
+		}
+	}
+}
+
+func (i *Decoder) Err() error {
+	if i.err == io.EOF {
+		return nil
+	}
+	return i.err
+}
+
+func (i *Decoder) Close() {
+	i.closer.Close()
+}
+
+type Config struct {
+	Stdin     io.Reader
+	Stdout    io.Writer
+	ProtoPath []string
+}
+
+// NewDecoder returns a stream of non-rooted data expressions. The encoding
+// type of f must be a data type, but does not have to be an encoding that
+// can stream. stdin is used in case the file is "-".
+func NewDecoder(f *build.File, cfg *Config) *Decoder {
+	r, err := reader(f, cfg.Stdin)
+	i := &Decoder{
+		closer:   r,
+		err:      err,
+		filename: f.Filename,
+		next: func() (ast.Expr, error) {
+			if err == nil {
+				err = io.EOF
+			}
+			return nil, io.EOF
+		},
+	}
+	if err != nil {
+		return i
+	}
+
+	path := f.Filename
+	switch f.Encoding {
+	case build.JSON, build.JSONL:
+		i.next = json.NewDecoder(nil, path, r).Extract
+		i.Next()
+	case build.YAML:
+		d, err := yaml.NewDecoder(path, r)
+		i.err = err
+		i.next = d.Decode
+		i.Next()
+	case build.Text:
+		b, err := ioutil.ReadAll(r)
+		i.err = err
+		i.expr = ast.NewString(string(b))
+	case build.Protobuf:
+		paths := &protobuf.Config{Paths: cfg.ProtoPath}
+		i.file, i.err = protobuf.Extract(path, r, paths)
+	default:
+		i.err = fmt.Errorf("unsupported stream type %q", f.Encoding)
+	}
+
+	return i
+}
+
+func reader(f *build.File, stdin io.Reader) (io.ReadCloser, error) {
+	switch s := f.Source.(type) {
+	case nil:
+		// Use the file name.
+	case string:
+		return ioutil.NopCloser(strings.NewReader(s)), nil
+	case []byte:
+		return ioutil.NopCloser(bytes.NewReader(s)), nil
+	case *bytes.Buffer:
+		// is io.Reader, but it needs to be readable repeatedly
+		if s != nil {
+			return ioutil.NopCloser(bytes.NewReader(s.Bytes())), nil
+		}
+	default:
+		return nil, fmt.Errorf("invalid source type %T", f.Source)
+	}
+	// TODO: should we allow this?
+	if f.Filename == "-" {
+		return ioutil.NopCloser(stdin), nil
+	}
+	return os.Open(f.Filename)
+}