cmd/cue/cmd: support importing .proto files
The import command now supports .proto files.
It generates a file foo.proto.cue for a file foo.proto.
Change-Id: I6670563c149f54b8d10ee550226403cb50e3e84d
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2001
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cmd/cue/cmd/import.go b/cmd/cue/cmd/import.go
index bc4bf2d..05f016d 100644
--- a/cmd/cue/cmd/import.go
+++ b/cmd/cue/cmd/import.go
@@ -38,6 +38,7 @@
"cuelang.org/go/cue/parser"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
+ "cuelang.org/go/internal/protobuf"
"cuelang.org/go/internal/third_party/yaml"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
@@ -51,9 +52,10 @@
The following file formats are currently supported:
- Format Extensions
- JSON .json .jsonl .ndjson
- YAML .yaml .yml
+ Format Extensions
+ JSON .json .jsonl .ndjson
+ YAML .yaml .yml
+ protobuf .proto
Files can either be specified explicitly, or inferred from the specified
packages. In either case, the file extension is replaced with .cue. It will
@@ -211,6 +213,8 @@
parseStrings = importCmd.Flags().BoolP("recursive", "R", false, "recursively parse string values")
importCmd.Flags().String("fix", "", "apply given fix")
+
+ protoPaths = importCmd.Flags().StringArrayP("proto_path", "I", nil, "paths in which to search for imports")
}
var (
@@ -223,18 +227,22 @@
list *bool
files *bool
parseStrings *bool
+ protoPaths *[]string
)
-type importFunc func(path string, r io.Reader) ([]ast.Expr, error)
+type importStreamFunc func(path string, r io.Reader) ([]ast.Expr, error)
+type importFileFunc func(path string, r io.Reader) (*ast.File, error)
type encodingInfo struct {
- fn importFunc
- typ string
+ fnStream importStreamFunc
+ fnFile importFileFunc
+ typ string
}
var (
- jsonEnc = &encodingInfo{handleJSON, "json"}
- yamlEnc = &encodingInfo{handleYAML, "yaml"}
+ jsonEnc = &encodingInfo{fnStream: handleJSON, typ: "json"}
+ yamlEnc = &encodingInfo{fnStream: handleYAML, typ: "yaml"}
+ protodefEnc = &encodingInfo{fnFile: handleProtoDef, typ: "proto"}
)
func getExtInfo(ext string) *encodingInfo {
@@ -247,6 +255,8 @@
return jsonEnc
case "yaml":
return yamlEnc
+ case "protobuf":
+ return protodefEnc
}
return nil
}
@@ -324,14 +334,42 @@
ext := filepath.Ext(filename)
handler := getExtInfo(ext)
- if handler == nil {
+ switch {
+ case handler == nil:
return fmt.Errorf("unsupported extension %q", ext)
+
+ case handler.fnFile != nil:
+ file, err := handler.fnFile(filename, f)
+ if err != nil {
+ return err
+ }
+ file.Filename = filename
+ return processFile(cmd, file)
+
+ case handler.fnStream != nil:
+ objs, err := handler.fnStream(filename, f)
+ if err != nil {
+ return err
+ }
+ return processStream(cmd, pkg, filename, objs)
+
+ default:
+ panic("incorrect handler")
}
- objs, err := handler.fn(filename, f)
- if err != nil {
+}
+
+func processFile(cmd *cobra.Command, file *ast.File) (err error) {
+ name := file.Filename + ".cue"
+
+ buf := &bytes.Buffer{}
+ if err := format.Node(buf, file); err != nil {
return err
}
+ return ioutil.WriteFile(name, buf.Bytes(), 0644)
+}
+
+func processStream(cmd *cobra.Command, pkg, filename string, objs []ast.Expr) error {
if *files {
for i, f := range objs {
err := combineExpressions(cmd, pkg, newName(filename, i), f)
@@ -619,6 +657,10 @@
return objects, nil
}
+func handleProtoDef(path string, r io.Reader) (f *ast.File, err error) {
+ return protobuf.Parse(path, r, &protobuf.Config{Paths: *protoPaths})
+}
+
type hoister struct {
fields map[string]bool
altNames map[string]*ast.Ident
diff --git a/cue/encoding/encoding.go b/cue/encoding/encoding.go
index c5f2ca6..8d4fe22 100644
--- a/cue/encoding/encoding.go
+++ b/cue/encoding/encoding.go
@@ -31,7 +31,7 @@
// All returns all known encodings.
func All() []*Encoding {
- return []*Encoding{jsonEnc, yamlEnc}
+ return []*Encoding{jsonEnc, yamlEnc, protodefEnc}
}
// MapExtension returns the likely encoding for a given file extension.
@@ -40,8 +40,9 @@
}
var (
- jsonEnc = &Encoding{name: "json"}
- yamlEnc = &Encoding{name: "yaml"}
+ jsonEnc = &Encoding{name: "json"}
+ yamlEnc = &Encoding{name: "yaml"}
+ protodefEnc = &Encoding{name: "protobuf"}
)
// extensions maps a file extension to a Kind.
@@ -51,4 +52,5 @@
".ndjson": jsonEnc,
".yaml": yamlEnc,
".yml": yamlEnc,
+ ".proto": protodefEnc,
}
diff --git a/internal/protobuf/parse.go b/internal/protobuf/parse.go
index 8b8ede9..19eb4ec 100644
--- a/internal/protobuf/parse.go
+++ b/internal/protobuf/parse.go
@@ -65,7 +65,7 @@
switch x := recover().(type) {
case nil:
case protoError:
- err = &ProtoError{
+ err = &Error{
Filename: filename,
Path: strings.Join(p.path, "."),
Err: x.error,
diff --git a/internal/protobuf/protobuf.go b/internal/protobuf/protobuf.go
index 4280d9c..958e6fb 100644
--- a/internal/protobuf/protobuf.go
+++ b/internal/protobuf/protobuf.go
@@ -52,16 +52,16 @@
return p.file, nil
}
-// ProtoError describes the location and cause of an error.
-type ProtoError struct {
+// Error describes the location and cause of an error.
+type Error struct {
Filename string
Path string
Err error
}
-func (p *ProtoError) Unwrap() error { return p.Err }
+func (p *Error) Unwrap() error { return p.Err }
-func (p *ProtoError) Error() string {
+func (p *Error) Error() string {
if p.Path == "" {
return fmt.Sprintf("parse of file %q failed: %v", p.Filename, p.Err)
}