2018-09-21 15:03:34 -07:00
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package internal_gengogrpc is internal to the protobuf module.
package internal_gengogrpc
import (
"fmt"
"strconv"
"strings"
2019-05-14 12:44:37 -07:00
"google.golang.org/protobuf/compiler/protogen"
2018-11-26 18:55:29 -08:00
2019-05-16 12:47:20 -07:00
"google.golang.org/protobuf/types/descriptorpb"
2018-09-21 15:03:34 -07:00
)
2018-12-03 11:12:10 -08:00
const (
contextPackage = protogen . GoImportPath ( "context" )
grpcPackage = protogen . GoImportPath ( "google.golang.org/grpc" )
)
2018-09-21 15:03:34 -07:00
// GenerateFile generates a _grpc.pb.go file containing gRPC service definitions.
2019-02-27 21:46:29 -08:00
func GenerateFile ( gen * protogen . Plugin , file * protogen . File ) * protogen . GeneratedFile {
2018-10-17 12:53:18 -07:00
if len ( file . Services ) == 0 {
2019-02-27 21:46:29 -08:00
return nil
2018-09-21 15:03:34 -07:00
}
2018-10-17 12:53:18 -07:00
filename := file . GeneratedFilenamePrefix + "_grpc.pb.go"
g := gen . NewGeneratedFile ( filename , file . GoImportPath )
2018-09-21 15:03:34 -07:00
g . P ( "// Code generated by protoc-gen-go-grpc. DO NOT EDIT." )
g . P ( )
2018-10-17 12:53:18 -07:00
g . P ( "package " , file . GoPackageName )
2018-09-21 15:03:34 -07:00
g . P ( )
2018-10-17 12:53:18 -07:00
GenerateFileContent ( gen , file , g )
2019-02-27 21:46:29 -08:00
return g
2018-09-21 15:03:34 -07:00
}
// GenerateFileContent generates the gRPC service definitions, excluding the package statement.
2018-10-17 12:53:18 -07:00
func GenerateFileContent ( gen * protogen . Plugin , file * protogen . File , g * protogen . GeneratedFile ) {
if len ( file . Services ) == 0 {
2018-09-21 15:03:34 -07:00
return
}
// TODO: Remove this. We don't need to include these references any more.
g . P ( "// Reference imports to suppress errors if they are not otherwise used." )
2018-12-03 11:12:10 -08:00
g . P ( "var _ " , contextPackage . Ident ( "Context" ) )
g . P ( "var _ " , grpcPackage . Ident ( "ClientConn" ) )
2018-09-21 15:03:34 -07:00
g . P ( )
g . P ( "// This is a compile-time assertion to ensure that this generated file" )
g . P ( "// is compatible with the grpc package it is being compiled against." )
2018-12-03 11:12:10 -08:00
g . P ( "const _ = " , grpcPackage . Ident ( "SupportPackageIsVersion4" ) )
2018-09-21 15:03:34 -07:00
g . P ( )
for _ , service := range file . Services {
genService ( gen , file , g , service )
}
}
2018-10-17 12:53:18 -07:00
func genService ( gen * protogen . Plugin , file * protogen . File , g * protogen . GeneratedFile , service * protogen . Service ) {
2018-09-21 15:03:34 -07:00
clientName := service . GoName + "Client"
g . P ( "// " , clientName , " is the client API for " , service . GoName , " service." )
g . P ( "//" )
g . P ( "// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream." )
// Client interface.
2018-11-26 18:55:29 -08:00
if service . Desc . Options ( ) . ( * descriptorpb . ServiceOptions ) . GetDeprecated ( ) {
2018-09-27 16:27:03 -07:00
g . P ( "//" )
g . P ( deprecationComment )
}
2018-10-04 12:42:37 -07:00
g . Annotate ( clientName , service . Location )
2018-09-21 15:03:34 -07:00
g . P ( "type " , clientName , " interface {" )
for _ , method := range service . Methods {
2018-10-17 12:53:18 -07:00
g . PrintLeadingComments ( method . Location )
2018-10-04 12:42:37 -07:00
g . Annotate ( clientName + "." + method . GoName , method . Location )
2018-09-21 15:03:34 -07:00
g . P ( clientSignature ( g , method ) )
}
g . P ( "}" )
g . P ( )
// Client structure.
g . P ( "type " , unexport ( clientName ) , " struct {" )
2018-12-03 11:12:10 -08:00
g . P ( "cc *" , grpcPackage . Ident ( "ClientConn" ) )
2018-09-21 15:03:34 -07:00
g . P ( "}" )
g . P ( )
// NewClient factory.
2018-11-26 18:55:29 -08:00
if service . Desc . Options ( ) . ( * descriptorpb . ServiceOptions ) . GetDeprecated ( ) {
2018-09-27 16:27:03 -07:00
g . P ( deprecationComment )
}
2018-12-03 11:12:10 -08:00
g . P ( "func New" , clientName , " (cc *" , grpcPackage . Ident ( "ClientConn" ) , ") " , clientName , " {" )
2018-09-21 15:03:34 -07:00
g . P ( "return &" , unexport ( clientName ) , "{cc}" )
g . P ( "}" )
g . P ( )
var methodIndex , streamIndex int
// Client method implementations.
for _ , method := range service . Methods {
if ! method . Desc . IsStreamingServer ( ) && ! method . Desc . IsStreamingClient ( ) {
// Unary RPC method
genClientMethod ( gen , file , g , method , methodIndex )
methodIndex ++
} else {
// Streaming RPC method
genClientMethod ( gen , file , g , method , streamIndex )
streamIndex ++
}
}
// Server interface.
serverType := service . GoName + "Server"
g . P ( "// " , serverType , " is the server API for " , service . GoName , " service." )
2018-11-26 18:55:29 -08:00
if service . Desc . Options ( ) . ( * descriptorpb . ServiceOptions ) . GetDeprecated ( ) {
2018-09-27 16:27:03 -07:00
g . P ( "//" )
g . P ( deprecationComment )
}
2018-10-04 12:42:37 -07:00
g . Annotate ( serverType , service . Location )
2018-09-21 15:03:34 -07:00
g . P ( "type " , serverType , " interface {" )
for _ , method := range service . Methods {
2018-10-17 12:53:18 -07:00
g . PrintLeadingComments ( method . Location )
2018-10-04 12:42:37 -07:00
g . Annotate ( serverType + "." + method . GoName , method . Location )
2018-09-21 15:03:34 -07:00
g . P ( serverSignature ( g , method ) )
}
g . P ( "}" )
g . P ( )
// Server registration.
2018-11-26 18:55:29 -08:00
if service . Desc . Options ( ) . ( * descriptorpb . ServiceOptions ) . GetDeprecated ( ) {
2018-09-27 16:27:03 -07:00
g . P ( deprecationComment )
}
2018-09-21 15:03:34 -07:00
serviceDescVar := "_" + service . GoName + "_serviceDesc"
2018-12-03 11:12:10 -08:00
g . P ( "func Register" , service . GoName , "Server(s *" , grpcPackage . Ident ( "Server" ) , ", srv " , serverType , ") {" )
2018-09-21 15:03:34 -07:00
g . P ( "s.RegisterService(&" , serviceDescVar , ` , srv) ` )
g . P ( "}" )
g . P ( )
// Server handler implementations.
var handlerNames [ ] string
for _ , method := range service . Methods {
hname := genServerMethod ( gen , file , g , method )
handlerNames = append ( handlerNames , hname )
}
// Service descriptor.
2018-12-03 11:12:10 -08:00
g . P ( "var " , serviceDescVar , " = " , grpcPackage . Ident ( "ServiceDesc" ) , " {" )
2018-09-21 15:03:34 -07:00
g . P ( "ServiceName: " , strconv . Quote ( string ( service . Desc . FullName ( ) ) ) , "," )
g . P ( "HandlerType: (*" , serverType , ")(nil)," )
2018-12-03 11:12:10 -08:00
g . P ( "Methods: []" , grpcPackage . Ident ( "MethodDesc" ) , "{" )
2018-09-21 15:03:34 -07:00
for i , method := range service . Methods {
if method . Desc . IsStreamingClient ( ) || method . Desc . IsStreamingServer ( ) {
continue
}
g . P ( "{" )
2018-12-11 09:24:55 -08:00
g . P ( "MethodName: " , strconv . Quote ( string ( method . Desc . Name ( ) ) ) , "," )
2018-09-21 15:03:34 -07:00
g . P ( "Handler: " , handlerNames [ i ] , "," )
g . P ( "}," )
}
g . P ( "}," )
2018-12-03 11:12:10 -08:00
g . P ( "Streams: []" , grpcPackage . Ident ( "StreamDesc" ) , "{" )
2018-09-21 15:03:34 -07:00
for i , method := range service . Methods {
if ! method . Desc . IsStreamingClient ( ) && ! method . Desc . IsStreamingServer ( ) {
continue
}
g . P ( "{" )
2018-12-11 09:24:55 -08:00
g . P ( "StreamName: " , strconv . Quote ( string ( method . Desc . Name ( ) ) ) , "," )
2018-09-21 15:03:34 -07:00
g . P ( "Handler: " , handlerNames [ i ] , "," )
if method . Desc . IsStreamingServer ( ) {
g . P ( "ServerStreams: true," )
}
if method . Desc . IsStreamingClient ( ) {
g . P ( "ClientStreams: true," )
}
g . P ( "}," )
}
g . P ( "}," )
g . P ( "Metadata: \"" , file . Desc . Path ( ) , "\"," )
g . P ( "}" )
g . P ( )
}
func clientSignature ( g * protogen . GeneratedFile , method * protogen . Method ) string {
2018-12-03 11:12:10 -08:00
s := method . GoName + "(ctx " + g . QualifiedGoIdent ( contextPackage . Ident ( "Context" ) )
2018-09-21 15:03:34 -07:00
if ! method . Desc . IsStreamingClient ( ) {
2019-04-15 23:39:09 -07:00
s += ", in *" + g . QualifiedGoIdent ( method . Input . GoIdent )
2018-09-21 15:03:34 -07:00
}
2018-12-03 11:12:10 -08:00
s += ", opts ..." + g . QualifiedGoIdent ( grpcPackage . Ident ( "CallOption" ) ) + ") ("
2018-09-21 15:03:34 -07:00
if ! method . Desc . IsStreamingClient ( ) && ! method . Desc . IsStreamingServer ( ) {
2019-04-15 23:39:09 -07:00
s += "*" + g . QualifiedGoIdent ( method . Output . GoIdent )
2018-09-21 15:03:34 -07:00
} else {
2019-04-15 23:39:09 -07:00
s += method . Parent . GoName + "_" + method . GoName + "Client"
2018-09-21 15:03:34 -07:00
}
s += ", error)"
return s
}
2018-10-17 12:53:18 -07:00
func genClientMethod ( gen * protogen . Plugin , file * protogen . File , g * protogen . GeneratedFile , method * protogen . Method , index int ) {
2019-04-15 23:39:09 -07:00
service := method . Parent
2018-09-21 15:03:34 -07:00
sname := fmt . Sprintf ( "/%s/%s" , service . Desc . FullName ( ) , method . Desc . Name ( ) )
2018-11-26 18:55:29 -08:00
if method . Desc . Options ( ) . ( * descriptorpb . MethodOptions ) . GetDeprecated ( ) {
2018-09-27 16:27:03 -07:00
g . P ( deprecationComment )
}
2018-09-21 15:03:34 -07:00
g . P ( "func (c *" , unexport ( service . GoName ) , "Client) " , clientSignature ( g , method ) , "{" )
if ! method . Desc . IsStreamingServer ( ) && ! method . Desc . IsStreamingClient ( ) {
2019-04-15 23:39:09 -07:00
g . P ( "out := new(" , method . Output . GoIdent , ")" )
2018-09-21 15:03:34 -07:00
g . P ( ` err := c.cc.Invoke(ctx, " ` , sname , ` ", in, out, opts...) ` )
g . P ( "if err != nil { return nil, err }" )
g . P ( "return out, nil" )
g . P ( "}" )
g . P ( )
return
}
streamType := unexport ( service . GoName ) + method . GoName + "Client"
serviceDescVar := "_" + service . GoName + "_serviceDesc"
g . P ( "stream, err := c.cc.NewStream(ctx, &" , serviceDescVar , ".Streams[" , index , ` ], " ` , sname , ` ", opts...) ` )
g . P ( "if err != nil { return nil, err }" )
g . P ( "x := &" , streamType , "{stream}" )
if ! method . Desc . IsStreamingClient ( ) {
g . P ( "if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }" )
g . P ( "if err := x.ClientStream.CloseSend(); err != nil { return nil, err }" )
}
g . P ( "return x, nil" )
g . P ( "}" )
g . P ( )
genSend := method . Desc . IsStreamingClient ( )
genRecv := method . Desc . IsStreamingServer ( )
genCloseAndRecv := ! method . Desc . IsStreamingServer ( )
// Stream auxiliary types and methods.
g . P ( "type " , service . GoName , "_" , method . GoName , "Client interface {" )
if genSend {
2019-04-15 23:39:09 -07:00
g . P ( "Send(*" , method . Input . GoIdent , ") error" )
2018-09-21 15:03:34 -07:00
}
if genRecv {
2019-04-15 23:39:09 -07:00
g . P ( "Recv() (*" , method . Output . GoIdent , ", error)" )
2018-09-21 15:03:34 -07:00
}
if genCloseAndRecv {
2019-04-15 23:39:09 -07:00
g . P ( "CloseAndRecv() (*" , method . Output . GoIdent , ", error)" )
2018-09-21 15:03:34 -07:00
}
2018-12-03 11:12:10 -08:00
g . P ( grpcPackage . Ident ( "ClientStream" ) )
2018-09-21 15:03:34 -07:00
g . P ( "}" )
g . P ( )
g . P ( "type " , streamType , " struct {" )
2018-12-03 11:12:10 -08:00
g . P ( grpcPackage . Ident ( "ClientStream" ) )
2018-09-21 15:03:34 -07:00
g . P ( "}" )
g . P ( )
if genSend {
2019-04-15 23:39:09 -07:00
g . P ( "func (x *" , streamType , ") Send(m *" , method . Input . GoIdent , ") error {" )
2018-09-21 15:03:34 -07:00
g . P ( "return x.ClientStream.SendMsg(m)" )
g . P ( "}" )
g . P ( )
}
if genRecv {
2019-04-15 23:39:09 -07:00
g . P ( "func (x *" , streamType , ") Recv() (*" , method . Output . GoIdent , ", error) {" )
g . P ( "m := new(" , method . Output . GoIdent , ")" )
2018-09-21 15:03:34 -07:00
g . P ( "if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }" )
g . P ( "return m, nil" )
g . P ( "}" )
g . P ( )
}
if genCloseAndRecv {
2019-04-15 23:39:09 -07:00
g . P ( "func (x *" , streamType , ") CloseAndRecv() (*" , method . Output . GoIdent , ", error) {" )
2018-09-21 15:03:34 -07:00
g . P ( "if err := x.ClientStream.CloseSend(); err != nil { return nil, err }" )
2019-04-15 23:39:09 -07:00
g . P ( "m := new(" , method . Output . GoIdent , ")" )
2018-09-21 15:03:34 -07:00
g . P ( "if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }" )
g . P ( "return m, nil" )
g . P ( "}" )
g . P ( )
}
}
func serverSignature ( g * protogen . GeneratedFile , method * protogen . Method ) string {
var reqArgs [ ] string
ret := "error"
if ! method . Desc . IsStreamingClient ( ) && ! method . Desc . IsStreamingServer ( ) {
2018-12-03 11:12:10 -08:00
reqArgs = append ( reqArgs , g . QualifiedGoIdent ( contextPackage . Ident ( "Context" ) ) )
2019-04-15 23:39:09 -07:00
ret = "(*" + g . QualifiedGoIdent ( method . Output . GoIdent ) + ", error)"
2018-09-21 15:03:34 -07:00
}
if ! method . Desc . IsStreamingClient ( ) {
2019-04-15 23:39:09 -07:00
reqArgs = append ( reqArgs , "*" + g . QualifiedGoIdent ( method . Input . GoIdent ) )
2018-09-21 15:03:34 -07:00
}
if method . Desc . IsStreamingClient ( ) || method . Desc . IsStreamingServer ( ) {
2019-04-15 23:39:09 -07:00
reqArgs = append ( reqArgs , method . Parent . GoName + "_" + method . GoName + "Server" )
2018-09-21 15:03:34 -07:00
}
return method . GoName + "(" + strings . Join ( reqArgs , ", " ) + ") " + ret
}
2018-10-17 12:53:18 -07:00
func genServerMethod ( gen * protogen . Plugin , file * protogen . File , g * protogen . GeneratedFile , method * protogen . Method ) string {
2019-04-15 23:39:09 -07:00
service := method . Parent
2018-09-21 15:03:34 -07:00
hname := fmt . Sprintf ( "_%s_%s_Handler" , service . GoName , method . GoName )
if ! method . Desc . IsStreamingClient ( ) && ! method . Desc . IsStreamingServer ( ) {
2018-12-03 11:12:10 -08:00
g . P ( "func " , hname , "(srv interface{}, ctx " , contextPackage . Ident ( "Context" ) , ", dec func(interface{}) error, interceptor " , grpcPackage . Ident ( "UnaryServerInterceptor" ) , ") (interface{}, error) {" )
2019-04-15 23:39:09 -07:00
g . P ( "in := new(" , method . Input . GoIdent , ")" )
2018-09-21 15:03:34 -07:00
g . P ( "if err := dec(in); err != nil { return nil, err }" )
g . P ( "if interceptor == nil { return srv.(" , service . GoName , "Server)." , method . GoName , "(ctx, in) }" )
2018-12-03 11:12:10 -08:00
g . P ( "info := &" , grpcPackage . Ident ( "UnaryServerInfo" ) , "{" )
2018-09-21 15:03:34 -07:00
g . P ( "Server: srv," )
2018-12-11 09:24:55 -08:00
g . P ( "FullMethod: " , strconv . Quote ( fmt . Sprintf ( "/%s/%s" , service . Desc . FullName ( ) , method . GoName ) ) , "," )
2018-09-21 15:03:34 -07:00
g . P ( "}" )
2018-12-03 11:12:10 -08:00
g . P ( "handler := func(ctx " , contextPackage . Ident ( "Context" ) , ", req interface{}) (interface{}, error) {" )
2019-04-15 23:39:09 -07:00
g . P ( "return srv.(" , service . GoName , "Server)." , method . GoName , "(ctx, req.(*" , method . Input . GoIdent , "))" )
2018-09-21 15:03:34 -07:00
g . P ( "}" )
g . P ( "return interceptor(ctx, in, info, handler)" )
g . P ( "}" )
g . P ( )
return hname
}
streamType := unexport ( service . GoName ) + method . GoName + "Server"
2018-12-03 11:12:10 -08:00
g . P ( "func " , hname , "(srv interface{}, stream " , grpcPackage . Ident ( "ServerStream" ) , ") error {" )
2018-09-21 15:03:34 -07:00
if ! method . Desc . IsStreamingClient ( ) {
2019-04-15 23:39:09 -07:00
g . P ( "m := new(" , method . Input . GoIdent , ")" )
2018-09-21 15:03:34 -07:00
g . P ( "if err := stream.RecvMsg(m); err != nil { return err }" )
g . P ( "return srv.(" , service . GoName , "Server)." , method . GoName , "(m, &" , streamType , "{stream})" )
} else {
g . P ( "return srv.(" , service . GoName , "Server)." , method . GoName , "(&" , streamType , "{stream})" )
}
g . P ( "}" )
g . P ( )
genSend := method . Desc . IsStreamingServer ( )
genSendAndClose := ! method . Desc . IsStreamingServer ( )
genRecv := method . Desc . IsStreamingClient ( )
// Stream auxiliary types and methods.
g . P ( "type " , service . GoName , "_" , method . GoName , "Server interface {" )
if genSend {
2019-04-15 23:39:09 -07:00
g . P ( "Send(*" , method . Output . GoIdent , ") error" )
2018-09-21 15:03:34 -07:00
}
if genSendAndClose {
2019-04-15 23:39:09 -07:00
g . P ( "SendAndClose(*" , method . Output . GoIdent , ") error" )
2018-09-21 15:03:34 -07:00
}
if genRecv {
2019-04-15 23:39:09 -07:00
g . P ( "Recv() (*" , method . Input . GoIdent , ", error)" )
2018-09-21 15:03:34 -07:00
}
2018-12-03 11:12:10 -08:00
g . P ( grpcPackage . Ident ( "ServerStream" ) )
2018-09-21 15:03:34 -07:00
g . P ( "}" )
g . P ( )
g . P ( "type " , streamType , " struct {" )
2018-12-03 11:12:10 -08:00
g . P ( grpcPackage . Ident ( "ServerStream" ) )
2018-09-21 15:03:34 -07:00
g . P ( "}" )
g . P ( )
if genSend {
2019-04-15 23:39:09 -07:00
g . P ( "func (x *" , streamType , ") Send(m *" , method . Output . GoIdent , ") error {" )
2018-09-21 15:03:34 -07:00
g . P ( "return x.ServerStream.SendMsg(m)" )
g . P ( "}" )
g . P ( )
}
if genSendAndClose {
2019-04-15 23:39:09 -07:00
g . P ( "func (x *" , streamType , ") SendAndClose(m *" , method . Output . GoIdent , ") error {" )
2018-09-21 15:03:34 -07:00
g . P ( "return x.ServerStream.SendMsg(m)" )
g . P ( "}" )
g . P ( )
}
if genRecv {
2019-04-15 23:39:09 -07:00
g . P ( "func (x *" , streamType , ") Recv() (*" , method . Input . GoIdent , ", error) {" )
g . P ( "m := new(" , method . Input . GoIdent , ")" )
2018-09-21 15:03:34 -07:00
g . P ( "if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }" )
g . P ( "return m, nil" )
g . P ( "}" )
g . P ( )
}
return hname
}
2018-09-27 16:27:03 -07:00
const deprecationComment = "// Deprecated: Do not use."
2018-09-21 15:03:34 -07:00
func unexport ( s string ) string { return strings . ToLower ( s [ : 1 ] ) + s [ 1 : ] }