cmd/protoc-gen-go-grpc: add gRPC code generator
This is a straight translation of the v1 API gRPC "plugin" to protogen.
Add a protoc-gen-go-grpc command. The preferred way to generate gRPC
services is to invoke both plugins separately:
protoc --go_out=. --go-grpc_out=. foo.proto
When invoked in this fashion, the generators will produce separate
foo.pb.go and foo_grpc.pb.go files.
Change-Id: Ie180385dab3da7063db96f7c2f9de3abbd749f63
Reviewed-on: https://go-review.googlesource.com/137037
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/protogen/protogen.go b/protogen/protogen.go
index 03a2ff0..87e643c 100644
--- a/protogen/protogen.go
+++ b/protogen/protogen.go
@@ -349,6 +349,7 @@
Messages []*Message // top-level message declarations
Enums []*Enum // top-level enum declarations
Extensions []*Extension // top-level extension declarations
+ Services []*Service // top-level service declarations
Generate bool // true if we should generate code for this file
// GeneratedFilenamePrefix is used to construct filenames for generated
@@ -401,6 +402,9 @@
for i, extdescs := 0, desc.Extensions(); i < extdescs.Len(); i++ {
f.Extensions = append(f.Extensions, newField(gen, f, nil, extdescs.Get(i)))
}
+ for i, sdescs := 0, desc.Services(); i < sdescs.Len(); i++ {
+ f.Services = append(f.Services, newService(gen, f, sdescs.Get(i)))
+ }
for _, message := range f.Messages {
if err := message.init(gen); err != nil {
return nil, err
@@ -411,6 +415,13 @@
return nil, err
}
}
+ for _, service := range f.Services {
+ for _, method := range service.Methods {
+ if err := method.init(gen); err != nil {
+ return nil, err
+ }
+ }
+ }
return f, nil
}
@@ -723,6 +734,68 @@
return g
}
+// A Service describes a service.
+type Service struct {
+ Desc protoreflect.ServiceDescriptor
+
+ GoName string
+ Path []int32 // location path of this service
+ Methods []*Method // service method definitions
+}
+
+func newService(gen *Plugin, f *File, desc protoreflect.ServiceDescriptor) *Service {
+ service := &Service{
+ Desc: desc,
+ GoName: camelCase(string(desc.Name())),
+ Path: []int32{fileServiceField, int32(desc.Index())},
+ }
+ for i, mdescs := 0, desc.Methods(); i < mdescs.Len(); i++ {
+ service.Methods = append(service.Methods, newMethod(gen, f, service, mdescs.Get(i)))
+ }
+ return service
+}
+
+// A Method describes a method in a service.
+type Method struct {
+ Desc protoreflect.MethodDescriptor
+
+ GoName string
+ ParentService *Service
+ Path []int32 // location path of this method
+ InputType *Message
+ OutputType *Message
+}
+
+func newMethod(gen *Plugin, f *File, service *Service, desc protoreflect.MethodDescriptor) *Method {
+ method := &Method{
+ Desc: desc,
+ GoName: camelCase(string(desc.Name())),
+ ParentService: service,
+ Path: pathAppend(service.Path, serviceMethodField, int32(desc.Index())),
+ }
+ return method
+}
+
+func (method *Method) init(gen *Plugin) error {
+ desc := method.Desc
+
+ inName := desc.InputType().FullName()
+ in, ok := gen.messagesByName[inName]
+ if !ok {
+ return fmt.Errorf("method %v: no descriptor for type %v", desc.FullName(), inName)
+ }
+ method.InputType = in
+
+ outName := desc.OutputType().FullName()
+ out, ok := gen.messagesByName[outName]
+ if !ok {
+ return fmt.Errorf("method %v: no descriptor for type %v", desc.FullName(), outName)
+ }
+ method.OutputType = out
+
+ return nil
+}
+
// P prints a line to the generated output. It converts each parameter to a
// string following the same rules as fmt.Print. It never inserts spaces
// between parameters.
@@ -843,6 +916,7 @@
filePackageField = 2 // package
fileMessageField = 4 // message_type
fileEnumField = 5 // enum_type
+ fileServiceField = 6 // service
fileExtensionField = 7 // extension
// field numbers in DescriptorProto
messageFieldField = 2 // field
@@ -852,6 +926,9 @@
messageOneofField = 8 // oneof_decl
// field numbers in EnumDescriptorProto
enumValueField = 2 // value
+ // field numbers in ServiceDescriptorProto
+ serviceMethodField = 2 // method
+ serviceStreamField = 4 // stream
)
// pathAppend appends elements to a location path.