diff options
author | Craig Tiller <ctiller@google.com> | 2015-01-13 16:11:30 -0800 |
---|---|---|
committer | Craig Tiller <ctiller@google.com> | 2015-01-13 16:11:30 -0800 |
commit | 375605b65f9d58b7544b4af8bc93d4cf8489f010 (patch) | |
tree | bc0e1a8861a3dfdb6e8b0bb43a219d31efb88064 /src/compiler/go_generator.cc | |
parent | 80fa15c15121a7d0ec020dec8bfa3697a96058b6 (diff) | |
parent | 49a06a6cb843b8ce592312c28b43c9dc527b99ee (diff) |
Merge github.com:google/grpc into api
Diffstat (limited to 'src/compiler/go_generator.cc')
-rw-r--r-- | src/compiler/go_generator.cc | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/src/compiler/go_generator.cc b/src/compiler/go_generator.cc new file mode 100644 index 0000000000..84aa27668e --- /dev/null +++ b/src/compiler/go_generator.cc @@ -0,0 +1,530 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/compiler/go_generator.h" + +#include <cctype> + +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream_impl_lite.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/descriptor.h> + +using namespace std; + +namespace grpc_go_generator { + +bool NoStreaming(const google::protobuf::MethodDescriptor* method) { + return !method->client_streaming() && !method->server_streaming(); +} + +bool ClientOnlyStreaming(const google::protobuf::MethodDescriptor* method) { + return method->client_streaming() && !method->server_streaming(); +} + +bool ServerOnlyStreaming(const google::protobuf::MethodDescriptor* method) { + return !method->client_streaming() && method->server_streaming(); +} + +bool BidiStreaming(const google::protobuf::MethodDescriptor* method) { + return method->client_streaming() && method->server_streaming(); +} + +bool HasClientOnlyStreaming(const google::protobuf::FileDescriptor* file) { + for (int i = 0; i < file->service_count(); i++) { + for (int j = 0; j < file->service(i)->method_count(); j++) { + if (ClientOnlyStreaming(file->service(i)->method(j))) { + return true; + } + } + } + return false; +} + +string LowerCaseService(const string& service) { + string ret = service; + if (!ret.empty() && ret[0] >= 'A' && ret[0] <= 'Z') { + ret[0] = ret[0] - 'A' + 'a'; + } + return ret; +} + +void PrintClientMethodDef(google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type()->name(); + (*vars)["Response"] = method->output_type()->name(); + if (NoStreaming(method)) { + printer->Print(*vars, + "\t$Method$(ctx context.Context, in *$Request$, opts " + "...rpc.CallOption) " + "(*$Response$, error)\n"); + } else if (BidiStreaming(method)) { + printer->Print(*vars, + "\t$Method$(ctx context.Context, opts ...rpc.CallOption) " + "($Service$_$Method$Client, error)\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "\t$Method$(ctx context.Context, m *$Request$, opts ...rpc.CallOption) " + "($Service$_$Method$Client, error)\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print(*vars, + "\t$Method$(ctx context.Context, opts ...rpc.CallOption) " + "($Service$_$Method$Client, error)\n"); + } +} + +void PrintClientMethodImpl(google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type()->name(); + (*vars)["Response"] = method->output_type()->name(); + + if (NoStreaming(method)) { + printer->Print( + *vars, + "func (c *$ServiceStruct$Client) $Method$(ctx context.Context, " + "in *$Request$, opts ...rpc.CallOption) (*$Response$, error) {\n"); + printer->Print(*vars, "\tout := new($Response$)\n"); + printer->Print(*vars, + "\terr := rpc.Invoke(ctx, \"/$Package$$Service$/$Method$\", " + "in, out, c.cc, opts...)\n"); + printer->Print("\tif err != nil {\n"); + printer->Print("\t\treturn nil, err\n"); + printer->Print("\t}\n"); + printer->Print("\treturn out, nil\n"); + printer->Print("}\n\n"); + } else if (BidiStreaming(method)) { + printer->Print( + *vars, + "func (c *$ServiceStruct$Client) $Method$(ctx context.Context, opts " + "...rpc.CallOption) ($Service$_$Method$Client, error) {\n" + "\tstream, err := rpc.NewClientStream(ctx, c.cc, " + "\"/$Package$$Service$/$Method$\", opts...)\n" + "\tif err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn &$ServiceStruct$$Method$Client{stream}, nil\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Client interface {\n" + "\tSend(*$Request$) error\n" + "\tRecv() (*$Response$, error)\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Client struct {\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Client) Send(m *$Request$) error {\n" + "\treturn x.ClientStream.SendProto(m)\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Client) Recv() (*$Response$, error) " + "{\n" + "\tm := new($Response$)\n" + "\tif err := x.ClientStream.RecvProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn m, nil\n" + "}\n\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "func (c *$ServiceStruct$Client) $Method$(ctx context.Context, m " + "*$Request$, " + "opts ...rpc.CallOption) ($Service$_$Method$Client, error) {\n" + "\tstream, err := rpc.NewClientStream(ctx, c.cc, " + "\"/$Package$$Service$/$Method$\", opts...)\n" + "\tif err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\tx := &$ServiceStruct$$Method$Client{stream}\n" + "\tif err := x.ClientStream.SendProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\tif err := x.ClientStream.CloseSend(); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn x, nil\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Client interface {\n" + "\tRecv() (*$Response$, error)\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Client struct {\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Client) Recv() (*$Response$, error) " + "{\n" + "\tm := new($Response$)\n" + "\tif err := x.ClientStream.RecvProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn m, nil\n" + "}\n\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "func (c *$ServiceStruct$Client) $Method$(ctx context.Context, opts " + "...rpc.CallOption) ($Service$_$Method$Client, error) {\n" + "\tstream, err := rpc.NewClientStream(ctx, c.cc, " + "\"/$Package$$Service$/$Method$\", opts...)\n" + "\tif err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn &$ServiceStruct$$Method$Client{stream}, nil\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Client interface {\n" + "\tSend(*$Request$) error\n" + "\tCloseAndRecv() (*$Response$, error)\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Client struct {\n" + "\trpc.ClientStream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Client) Send(m *$Request$) error {\n" + "\treturn x.ClientStream.SendProto(m)\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Client) CloseAndRecv() (*$Response$, " + "error) {\n" + "\tif err := x.ClientStream.CloseSend(); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\tm := new($Response$)\n" + "\tif err := x.ClientStream.RecvProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\t// Read EOF.\n" + "\tif err := x.ClientStream.RecvProto(m); err == io.EOF {\n" + "\t\treturn m, io.EOF\n" + "\t}\n" + "\t// gRPC protocol violation.\n" + "\treturn m, fmt.Errorf(\"Violate gRPC client streaming protocol: no " + "EOF after the response.\")\n" + "}\n\n"); + } +} + +void PrintClient(google::protobuf::io::Printer* printer, + const google::protobuf::ServiceDescriptor* service, + map<string, string>* vars) { + (*vars)["Service"] = service->name(); + (*vars)["ServiceStruct"] = LowerCaseService(service->name()); + printer->Print(*vars, "type $Service$Client interface {\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintClientMethodDef(printer, service->method(i), vars); + } + printer->Print("}\n\n"); + + printer->Print(*vars, + "type $ServiceStruct$Client struct {\n" + "\tcc *rpc.ClientConn\n" + "}\n\n"); + printer->Print( + *vars, + "func New$Service$Client(cc *rpc.ClientConn) $Service$Client {\n" + "\treturn &$ServiceStruct$Client{cc}\n" + "}\n\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintClientMethodImpl(printer, service->method(i), vars); + } +} + +void PrintServerMethodDef(google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type()->name(); + (*vars)["Response"] = method->output_type()->name(); + if (NoStreaming(method)) { + printer->Print( + *vars, + "\t$Method$(context.Context, *$Request$) (*$Response$, error)\n"); + } else if (BidiStreaming(method)) { + printer->Print(*vars, "\t$Method$($Service$_$Method$Server) error\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print(*vars, + "\t$Method$(*$Request$, $Service$_$Method$Server) error\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print(*vars, "\t$Method$($Service$_$Method$Server) error\n"); + } +} + +void PrintServerHandler(google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type()->name(); + (*vars)["Response"] = method->output_type()->name(); + if (NoStreaming(method)) { + printer->Print( + *vars, + "func _$Service$_$Method$_Handler(srv interface{}, ctx context.Context," + " buf []byte) (proto.Message, error) {\n"); + printer->Print(*vars, "\tin := new($Request$)\n"); + printer->Print("\tif err := proto.Unmarshal(buf, in); err != nil {\n"); + printer->Print("\t\treturn nil, err\n"); + printer->Print("\t}\n"); + printer->Print(*vars, + "\tout, err := srv.($Service$Server).$Method$(ctx, in)\n"); + printer->Print("\tif err != nil {\n"); + printer->Print("\t\treturn nil, err\n"); + printer->Print("\t}\n"); + printer->Print("\treturn out, nil\n"); + printer->Print("}\n\n"); + } else if (BidiStreaming(method)) { + printer->Print( + *vars, + "func _$Service$_$Method$_Handler(srv interface{}, stream rpc.Stream) " + "error {\n" + "\treturn srv.($Service$Server).$Method$(&$ServiceStruct$$Method$Server" + "{stream})\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Server interface {\n" + "\tSend(*$Response$) error\n" + "\tRecv() (*$Request$, error)\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Server struct {\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Server) Send(m *$Response$) error {\n" + "\treturn x.Stream.SendProto(m)\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Server) Recv() (*$Request$, error) " + "{\n" + "\tm := new($Request$)\n" + "\tif err := x.Stream.RecvProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn m, nil\n" + "}\n\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "func _$Service$_$Method$_Handler(srv interface{}, stream rpc.Stream) " + "error {\n" + "\tm := new($Request$)\n" + "\tif err := stream.RecvProto(m); err != nil {\n" + "\t\treturn err\n" + "\t}\n" + "\treturn srv.($Service$Server).$Method$(m, " + "&$ServiceStruct$$Method$Server{stream})\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Server interface {\n" + "\tSend(*$Response$) error\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Server struct {\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Server) Send(m *$Response$) error {\n" + "\treturn x.Stream.SendProto(m)\n" + "}\n\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "func _$Service$_$Method$_Handler(srv interface{}, stream rpc.Stream) " + "error {\n" + "\treturn srv.($Service$Server).$Method$(&$ServiceStruct$$Method$Server" + "{stream})\n" + "}\n\n"); + printer->Print(*vars, + "type $Service$_$Method$Server interface {\n" + "\tSendAndClose(*$Response$) error\n" + "\tRecv() (*$Request$, error)\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print(*vars, + "type $ServiceStruct$$Method$Server struct {\n" + "\trpc.Stream\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Server) SendAndClose(m *$Response$) " + "error {\n" + "\tif err := x.Stream.SendProto(m); err != nil {\n" + "\t\treturn err\n" + "\t}\n" + "\treturn nil\n" + "}\n\n"); + printer->Print( + *vars, + "func (x *$ServiceStruct$$Method$Server) Recv() (*$Request$, error) {\n" + "\tm := new($Request$)\n" + "\tif err := x.Stream.RecvProto(m); err != nil {\n" + "\t\treturn nil, err\n" + "\t}\n" + "\treturn m, nil\n" + "}\n\n"); + } +} + +void PrintServerMethodDesc(google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + printer->Print("\t\t{\n"); + printer->Print(*vars, "\t\t\tMethodName:\t\"$Method$\",\n"); + printer->Print(*vars, "\t\t\tHandler:\t_$Service$_$Method$_Handler,\n"); + printer->Print("\t\t},\n"); +} + +void PrintServerStreamingMethodDesc( + google::protobuf::io::Printer* printer, + const google::protobuf::MethodDescriptor* method, + map<string, string>* vars) { + (*vars)["Method"] = method->name(); + printer->Print("\t\t{\n"); + printer->Print(*vars, "\t\t\tStreamName:\t\"$Method$\",\n"); + printer->Print(*vars, "\t\t\tHandler:\t_$Service$_$Method$_Handler,\n"); + printer->Print("\t\t},\n"); +} + +void PrintServer(google::protobuf::io::Printer* printer, + const google::protobuf::ServiceDescriptor* service, + map<string, string>* vars) { + (*vars)["Service"] = service->name(); + printer->Print(*vars, "type $Service$Server interface {\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintServerMethodDef(printer, service->method(i), vars); + } + printer->Print("}\n\n"); + + printer->Print(*vars, + "func RegisterService(s *rpc.Server, srv $Service$Server) {\n" + "\ts.RegisterService(&_$Service$_serviceDesc, srv)\n" + "}\n\n"); + + for (int i = 0; i < service->method_count(); ++i) { + PrintServerHandler(printer, service->method(i), vars); + } + + printer->Print(*vars, + "var _$Service$_serviceDesc = rpc.ServiceDesc{\n" + "\tServiceName: \"$Package$$Service$\",\n" + "\tHandlerType: (*$Service$Server)(nil),\n" + "\tMethods: []rpc.MethodDesc{\n"); + for (int i = 0; i < service->method_count(); ++i) { + if (NoStreaming(service->method(i))) { + PrintServerMethodDesc(printer, service->method(i), vars); + } + } + printer->Print("\t},\n"); + + printer->Print("\tStreams: []rpc.StreamDesc{\n"); + for (int i = 0; i < service->method_count(); ++i) { + if (!NoStreaming(service->method(i))) { + PrintServerStreamingMethodDesc(printer, service->method(i), vars); + } + } + printer->Print( + "\t},\n" + "}\n\n"); +} + +std::string BadToUnderscore(std::string str) { + for (unsigned i = 0; i < str.size(); ++i) { + if (!std::isalnum(str[i])) { + str[i] = '_'; + } + } + return str; +} + +string GetServices(const google::protobuf::FileDescriptor* file) { + string output; + google::protobuf::io::StringOutputStream output_stream(&output); + google::protobuf::io::Printer printer(&output_stream, '$'); + map<string, string> vars; + + string package_name = !file->options().go_package().empty() + ? file->options().go_package() + : file->package(); + vars["PackageName"] = BadToUnderscore(package_name); + printer.Print(vars, "package $PackageName$\n\n"); + printer.Print("import (\n"); + if (HasClientOnlyStreaming(file)) { + printer.Print( + "\t\"fmt\"\n" + "\t\"io\"\n"); + } + printer.Print( + "\t\"google/net/grpc/go/rpc\"\n" + "\tcontext \"google/third_party/golang/go_net/context/context\"\n" + "\tproto \"google/net/proto2/go/proto\"\n" + ")\n\n"); + + // $Package$ is used to fully qualify method names. + vars["Package"] = file->package(); + if (!file->package().empty()) { + vars["Package"].append("."); + } + + for (int i = 0; i < file->service_count(); ++i) { + PrintClient(&printer, file->service(0), &vars); + printer.Print("\n"); + PrintServer(&printer, file->service(0), &vars); + printer.Print("\n"); + } + return output; +} + +} // namespace grpc_go_generator |