encoding/protobuf: provide default import path and short name
Put a proto file in googleapis.com by default if the
go_packagname is not provided.
googleapis.com without a subdomain is an invalid
domain and should thus be safe to use and will
always be resolved within a local pkg directory.
Alternatively, I could name it proto.cuelang.org
or proto.googleapis.com.
Note that the go_package mechanism is still
necessary to be able to determine a specific
destination of a package, for instance if it needs
to be merged with other CUE files within the same
directory (as the genoapi tool does).
Change-Id: I65484a0c940409b024160fa5d086bf27e3500894
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2820
Reviewed-by: Jason Wang <jasonwzm@google.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/protobuf/parse.go b/encoding/protobuf/parse.go
index 3c8713d..9647df4 100644
--- a/encoding/protobuf/parse.go
+++ b/encoding/protobuf/parse.go
@@ -101,16 +101,26 @@
failf(x.Position, "unquoting package filed: %v", err)
}
split := strings.Split(str, ";")
- p.goPkgPath = split[0]
- switch len(split) {
- case 1:
- p.goPkg = path.Base(str)
- case 2:
- p.goPkg = split[1]
+ switch {
+ case strings.Contains(split[0], "."):
+ p.cuePkgPath = split[0]
+ switch len(split) {
+ case 1:
+ p.shortPkgName = path.Base(str)
+ case 2:
+ p.shortPkgName = split[1]
+ default:
+ failf(x.Position, "unexpected ';' in %q", str)
+ }
+ p.file.Name = ast.NewIdent(p.shortPkgName)
+
+ case len(split) == 1:
+ p.shortPkgName = split[0]
+ p.file.Name = ast.NewIdent(p.shortPkgName)
+
default:
- failf(x.Position, "unexpected ';' in %q", str)
+ failf(x.Position, "malformed go_package clause %s", str)
}
- p.file.Name = ast.NewIdent(p.goPkg)
// name.AddComment(comment(x.Comment, true))
// name.AddComment(comment(x.InlineComment, false))
}
@@ -163,10 +173,10 @@
proto3 bool
- id string
- protoPkg string
- goPkg string
- goPkgPath string
+ id string
+ protoPkg string
+ shortPkgName string
+ cuePkgPath string
// w bytes.Buffer
file *ast.File
@@ -187,6 +197,23 @@
pkg *protoConverter
}
+func (p *protoConverter) importPath() string {
+ if p.cuePkgPath == "" && p.protoPkg != "" {
+ dir := strings.Replace(p.protoPkg, ".", "/", -1)
+ p.cuePkgPath = path.Join("googleapis.com", dir)
+ }
+ return p.cuePkgPath
+}
+
+func (p *protoConverter) shortName() string {
+ if p.shortPkgName == "" && p.protoPkg != "" {
+ split := strings.Split(p.protoPkg, ".")
+ p.shortPkgName = split[len(split)-1]
+ p.file.Name = ast.NewIdent(p.shortPkgName)
+ }
+ return p.shortPkgName
+}
+
func (p *protoConverter) toCUEPos(pos scanner.Position) token.Pos {
return p.tfile.Pos(pos.Offset, 0)
}
@@ -297,7 +324,7 @@
}
if m, ok := p.scope[0][name[:i]]; ok {
if m.pkg != nil {
- p.imported[m.pkg.goPkgPath] = true
+ p.imported[m.pkg.importPath()] = true
// TODO: do something more principled.
}
cueName := strings.Replace(name[i:], ".", "_", -1)
@@ -340,8 +367,8 @@
}
prefix := ""
- if imp.goPkgPath != p.goPkgPath {
- prefix = imp.goPkg + "."
+ if imp.importPath() != p.importPath() {
+ prefix = imp.shortName() + "."
}
pkgNamespace := strings.Split(imp.protoPkg, ".")
@@ -354,7 +381,7 @@
}
if _, ok := p.scope[0][ref]; !ok {
pkg := imp
- if imp.goPkgPath == p.goPkgPath {
+ if imp.importPath() == p.importPath() {
pkg = nil
}
p.scope[0][ref] = mapping{prefix + k, "", pkg}
diff --git a/encoding/protobuf/protobuf.go b/encoding/protobuf/protobuf.go
index a2a1413..0baed41 100644
--- a/encoding/protobuf/protobuf.go
+++ b/encoding/protobuf/protobuf.go
@@ -16,8 +16,24 @@
// definitions and instances.
//
// Proto definition mapping follows the guidelines of mapping Proto to JSON as
-// discussed in https://developers.google.com/protocol-buffers/docs/proto3,
-// and carries some of the mapping further when possible with CUE.
+// discussed in https://developers.google.com/protocol-buffers/docs/proto3, and
+// carries some of the mapping further when possible with CUE.
+//
+//
+// Package Paths
+//
+// If a .proto file contains a go_package directive, it will be used as the
+// destination package fo the generated .cue files. A common use case is to
+// generate the CUE in the same directory as the .proto definition. If a
+// destination package is not within the current CUE module, it will be written
+// relative to the pkg directory.
+//
+// If a .proto file does not specify go_package, it will convert a proto package
+// "google.parent.sub" to the import path "googleapis.com/google/parent/sub".
+// It is safe to mix package with and without a go_package within the same
+// project.
+//
+// Type Mappings
//
// The following type mappings of defintions apply:
//
@@ -49,8 +65,8 @@
// Timestamp time.Time See struct.proto.
// Duration time.Duration See struct.proto.
//
-// Protobuf definitions can be annotated with CUE constraints that are
-// included in the generated CUE:
+// Protobuf definitions can be annotated with CUE constraints that are included
+// in the generated CUE:
// (cue.val) string CUE expression defining a constraint for this
// field. The string may refer to other fields
// in a message definition using their JSON name.
@@ -286,10 +302,10 @@
if b.errs != nil {
return nil
}
- importPath := p.goPkgPath
+ importPath := p.importPath()
if importPath == "" {
err := errors.Newf(token.NoPos,
- "no go_package for proto package %q in file %s", p.id, p.file.Filename)
+ "no package clause for proto package %q in file %s", p.id, p.file.Filename)
b.errs = errors.Append(b.errs, err)
// TODO: find an alternative. Is proto package good enough?
return nil
@@ -320,7 +336,7 @@
Root: b.root,
Dir: dir,
ImportPath: importPath,
- PkgName: p.goPkg,
+ PkgName: p.shortPkgName,
DisplayPath: p.protoPkg,
}
b.imports[importPath] = inst
diff --git a/encoding/protobuf/testdata/acme/test.proto b/encoding/protobuf/testdata/acme/test.proto
new file mode 100644
index 0000000..64de468
--- /dev/null
+++ b/encoding/protobuf/testdata/acme/test.proto
@@ -0,0 +1,7 @@
+syntax = "proto3";
+
+package acme.test;
+
+message Test {
+ int32 test = 1;
+}
diff --git a/encoding/protobuf/testdata/acme/test/test.proto b/encoding/protobuf/testdata/acme/test/test.proto
new file mode 100644
index 0000000..186ce1a
--- /dev/null
+++ b/encoding/protobuf/testdata/acme/test/test.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+package acme.test.test;
+
+// Override the short name only of this package. This notation is seen in some
+// gogoproto files.
+option go_package = "test_test"
+
+message AnotherTest {
+ int32 test = 1;
+}
diff --git a/encoding/protobuf/testdata/attributes.proto.out.cue b/encoding/protobuf/testdata/attributes.proto.out.cue
index d678760..4911be4 100644
--- a/encoding/protobuf/testdata/attributes.proto.out.cue
+++ b/encoding/protobuf/testdata/attributes.proto.out.cue
@@ -14,7 +14,11 @@
// limitations under the License.
package v1
-import "time"
+import (
+ "googleapis.com/acme/test"
+ "googleapis.com/acme/test/test"
+ "time"
+)
StructWrap: {
struct?: {} @protobuf(1,type=google.protobuf.Struct)
@@ -42,24 +46,6 @@
// target.service: example
// ```
//
-// A given Istio deployment has a fixed vocabulary of attributes that it understands.
-// The specific vocabulary is determined by the set of attribute producers being used
-// in the deployment. The primary attribute producer in Istio is Envoy, although
-// specialized Mixer adapters and services can also generate attributes.
-//
-// The common baseline set of attributes available in most Istio deployments is defined
-// [here](https://istio.io/docs/reference/config/policy-and-telemetry/attribute-vocabulary/).
-//
-// Attributes are strongly typed. The supported attribute types are defined by
-// [ValueType](https://github.com/istio/api/blob/master/policy/v1beta1/value_type.proto).
-// Each type of value is encoded into one of the so-called transport types present
-// in this message.
-//
-// Defines a map of attributes in uncompressed format.
-// Following places may use this message:
-// 1) Configure Istio/Proxy with static per-proxy attributes, such as source.uid.
-// 2) Service IDL definition to extract api attributes for active requests.
-// 3) Forward attributes from client proxy to server proxy for HTTP requests.
Attributes: {
// A map of attribute name to its value.
attributes: {
@@ -95,6 +81,10 @@
} | {
// Used for values of type STRING_MAP
stringMapValue: Attributes_StringMap @protobuf(9,type=StringMap,name=string_map_value)
+} | {
+ testValue: test.Test @protobuf(10,type=acme.test.Test,name=test_value)
+} | {
+ testValue: test_test.AnotherTest @protobuf(11,type=acme.test.test.AnotherTest,name=test_value)
}
// Defines a string map.
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes.proto b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes.proto
index ddc1b94..83c9644 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes.proto
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes.proto
@@ -22,6 +22,8 @@
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/struct.proto";
+import "acme/test.proto";
+import "acme/test/test.proto";
option (gogoproto.goproto_getters_all) = false;
option (gogoproto.equal_all) = false;
@@ -56,24 +58,6 @@
// target.service: example
// ```
//
-// A given Istio deployment has a fixed vocabulary of attributes that it understands.
-// The specific vocabulary is determined by the set of attribute producers being used
-// in the deployment. The primary attribute producer in Istio is Envoy, although
-// specialized Mixer adapters and services can also generate attributes.
-//
-// The common baseline set of attributes available in most Istio deployments is defined
-// [here](https://istio.io/docs/reference/config/policy-and-telemetry/attribute-vocabulary/).
-//
-// Attributes are strongly typed. The supported attribute types are defined by
-// [ValueType](https://github.com/istio/api/blob/master/policy/v1beta1/value_type.proto).
-// Each type of value is encoded into one of the so-called transport types present
-// in this message.
-//
-// Defines a map of attributes in uncompressed format.
-// Following places may use this message:
-// 1) Configure Istio/Proxy with static per-proxy attributes, such as source.uid.
-// 2) Service IDL definition to extract api attributes for active requests.
-// 3) Forward attributes from client proxy to server proxy for HTTP requests.
message Attributes {
// A map of attribute name to its value.
map<string, AttributeValue> attributes = 1;
@@ -105,6 +89,9 @@
// Used for values of type STRING_MAP
StringMap string_map_value = 9;
+
+ acme.test.Test test_value = 10;
+ acme.test.test.AnotherTest test_value = 11;
}
}
diff --git a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
index feced59..cf13972 100644
--- a/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
+++ b/encoding/protobuf/testdata/istio.io/api/mixer/v1/attributes_proto_gen.cue
@@ -14,7 +14,11 @@
// limitations under the License.
package v1
-import "time"
+import (
+ "googleapis.com/acme/test"
+ "googleapis.com/acme/test/test"
+ "time"
+)
StructWrap: {
struct?: {} @protobuf(1,type=google.protobuf.Struct)
@@ -42,24 +46,6 @@
// target.service: example
// ```
//
-// A given Istio deployment has a fixed vocabulary of attributes that it understands.
-// The specific vocabulary is determined by the set of attribute producers being used
-// in the deployment. The primary attribute producer in Istio is Envoy, although
-// specialized Mixer adapters and services can also generate attributes.
-//
-// The common baseline set of attributes available in most Istio deployments is defined
-// [here](https://istio.io/docs/reference/config/policy-and-telemetry/attribute-vocabulary/).
-//
-// Attributes are strongly typed. The supported attribute types are defined by
-// [ValueType](https://github.com/istio/api/blob/master/policy/v1beta1/value_type.proto).
-// Each type of value is encoded into one of the so-called transport types present
-// in this message.
-//
-// Defines a map of attributes in uncompressed format.
-// Following places may use this message:
-// 1) Configure Istio/Proxy with static per-proxy attributes, such as source.uid.
-// 2) Service IDL definition to extract api attributes for active requests.
-// 3) Forward attributes from client proxy to server proxy for HTTP requests.
Attributes: {
// A map of attribute name to its value.
attributes: {
@@ -95,6 +81,10 @@
} | {
// Used for values of type STRING_MAP
stringMapValue: Attributes_StringMap @protobuf(9,type=StringMap,name=string_map_value)
+} | {
+ testValue: test.Test @protobuf(10,type=acme.test.Test,name=test_value)
+} | {
+ testValue: test_test.AnotherTest @protobuf(11,type=acme.test.test.AnotherTest,name=test_value)
}
// Defines a string map.
diff --git a/encoding/protobuf/testdata/istio.io/api/pkg/googleapis.com/acme/test/test/test_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/pkg/googleapis.com/acme/test/test/test_proto_gen.cue
new file mode 100644
index 0000000..efe315a
--- /dev/null
+++ b/encoding/protobuf/testdata/istio.io/api/pkg/googleapis.com/acme/test/test/test_proto_gen.cue
@@ -0,0 +1,5 @@
+package test_test
+
+AnotherTest: {
+ test?: int32 @protobuf(1)
+}
diff --git a/encoding/protobuf/testdata/istio.io/api/pkg/googleapis.com/acme/test/test_proto_gen.cue b/encoding/protobuf/testdata/istio.io/api/pkg/googleapis.com/acme/test/test_proto_gen.cue
new file mode 100644
index 0000000..329db3e
--- /dev/null
+++ b/encoding/protobuf/testdata/istio.io/api/pkg/googleapis.com/acme/test/test_proto_gen.cue
@@ -0,0 +1,5 @@
+package test
+
+Test: {
+ test?: int32 @protobuf(1)
+}
diff --git a/encoding/protobuf/types.go b/encoding/protobuf/types.go
index 2191afe..687819b 100644
--- a/encoding/protobuf/types.go
+++ b/encoding/protobuf/types.go
@@ -53,8 +53,8 @@
}
var (
- pkgTime = &protoConverter{goPkgPath: "time"}
- pkgStruct = &protoConverter{goPkgPath: "struct"}
+ pkgTime = &protoConverter{cuePkgPath: "time"}
+ pkgStruct = &protoConverter{cuePkgPath: "struct"}
)
func (p *protoConverter) mapBuiltinPackage(pos scanner.Position, file string, required bool) (generate bool) {