mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-05 13:08:58 +00:00
506 lines
18 KiB
C++
506 lines
18 KiB
C++
#include "src/compiler/go_generator.h"
|
|
|
|
#include <cctype>
|
|
#include <map>
|
|
#include <sstream>
|
|
|
|
template<class T> grpc::string as_string(T x) {
|
|
std::ostringstream out;
|
|
out << x;
|
|
return out.str();
|
|
}
|
|
|
|
inline bool ClientOnlyStreaming(const grpc_generator::Method *method) {
|
|
return method->ClientStreaming() && !method->ServerStreaming();
|
|
}
|
|
|
|
inline bool ServerOnlyStreaming(const grpc_generator::Method *method) {
|
|
return !method->ClientStreaming() && method->ServerStreaming();
|
|
}
|
|
|
|
namespace grpc_go_generator {
|
|
|
|
// Returns string with first letter to lowerCase
|
|
grpc::string unexportName(grpc::string s) {
|
|
if (s.empty()) return s;
|
|
s[0] = static_cast<char>(std::tolower(s[0]));
|
|
return s;
|
|
}
|
|
|
|
// Returns string with first letter to uppercase
|
|
grpc::string exportName(grpc::string s) {
|
|
if (s.empty()) return s;
|
|
s[0] = static_cast<char>(std::toupper(s[0]));
|
|
return s;
|
|
}
|
|
|
|
void GenerateError(grpc_generator::Printer *printer,
|
|
std::map<grpc::string, grpc::string> vars,
|
|
const bool multiple_return = true) {
|
|
printer->Print(vars, "if $Error_Check$ {\n");
|
|
printer->Indent();
|
|
vars["Return"] = multiple_return ? "nil, err" : "err";
|
|
printer->Print(vars, "return $Return$\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
}
|
|
|
|
// Generates imports for the service
|
|
void GenerateImports(grpc_generator::File *file,
|
|
grpc_generator::Printer *printer,
|
|
std::map<grpc::string, grpc::string> vars) {
|
|
vars["filename"] = file->filename();
|
|
printer->Print("//Generated by gRPC Go plugin\n");
|
|
printer->Print("//If you make any local changes, they will be lost\n");
|
|
printer->Print(vars, "//source: $filename$\n\n");
|
|
printer->Print(vars, "package $Package$\n\n");
|
|
printer->Print("import (\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "$context$ \"context\"\n");
|
|
printer->Print("flatbuffers \"github.com/google/flatbuffers/go\"\n");
|
|
printer->Print(vars, "$grpc$ \"google.golang.org/grpc\"\n");
|
|
printer->Print("\"google.golang.org/grpc/codes\"\n");
|
|
printer->Print("\"google.golang.org/grpc/status\"\n");
|
|
printer->Outdent();
|
|
printer->Print(")\n\n");
|
|
}
|
|
|
|
// Generates Server method signature source
|
|
void GenerateServerMethodSignature(const grpc_generator::Method *method,
|
|
grpc_generator::Printer *printer,
|
|
std::map<grpc::string, grpc::string> vars) {
|
|
vars["Method"] = exportName(method->name());
|
|
vars["Request"] = method->get_input_type_name();
|
|
vars["Response"] = (vars["CustomMethodIO"] == "")
|
|
? method->get_output_type_name()
|
|
: vars["CustomMethodIO"];
|
|
if (method->NoStreaming()) {
|
|
printer->Print(
|
|
vars,
|
|
"$Method$($context$.Context, *$Request$) (*$Response$, error)$Ending$");
|
|
} else if (ServerOnlyStreaming(method)) {
|
|
printer->Print(
|
|
vars, "$Method$(*$Request$, $Service$_$Method$Server) error$Ending$");
|
|
} else {
|
|
printer->Print(vars, "$Method$($Service$_$Method$Server) error$Ending$");
|
|
}
|
|
}
|
|
|
|
void GenerateServerMethod(const grpc_generator::Method *method,
|
|
grpc_generator::Printer *printer,
|
|
std::map<grpc::string, grpc::string> vars) {
|
|
vars["Method"] = exportName(method->name());
|
|
vars["Request"] = method->get_input_type_name();
|
|
vars["Response"] = (vars["CustomMethodIO"] == "")
|
|
? method->get_output_type_name()
|
|
: vars["CustomMethodIO"];
|
|
vars["FullMethodName"] =
|
|
"/" + vars["ServicePrefix"] + vars["Service"] + "/" + vars["Method"];
|
|
vars["Handler"] = "_" + vars["Service"] + "_" + vars["Method"] + "_Handler";
|
|
if (method->NoStreaming()) {
|
|
printer->Print(
|
|
vars,
|
|
"func $Handler$(srv interface{}, ctx $context$.Context,\n\tdec "
|
|
"func(interface{}) error, interceptor $grpc$.UnaryServerInterceptor) "
|
|
"(interface{}, error) {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "in := new($Request$)\n");
|
|
vars["Error_Check"] = "err := dec(in); err != nil";
|
|
GenerateError(printer, vars);
|
|
printer->Print("if interceptor == nil {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "return srv.($Service$Server).$Method$(ctx, in)\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Print(vars, "info := &$grpc$.UnaryServerInfo{\n");
|
|
printer->Indent();
|
|
printer->Print("Server: srv,\n");
|
|
printer->Print(vars, "FullMethod: \"$FullMethodName$\",\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Outdent();
|
|
printer->Print("\n");
|
|
printer->Indent();
|
|
printer->Print(vars,
|
|
"handler := func(ctx $context$.Context, req interface{}) "
|
|
"(interface{}, error) {\n");
|
|
printer->Indent();
|
|
printer->Print(
|
|
vars, "return srv.($Service$Server).$Method$(ctx, req.(*$Request$))\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Print("return interceptor(ctx, in, info, handler)\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
return;
|
|
}
|
|
vars["StreamType"] = vars["ServiceUnexported"] + vars["Method"] + "Server";
|
|
printer->Print(
|
|
vars,
|
|
"func $Handler$(srv interface{}, stream $grpc$.ServerStream) error {\n");
|
|
printer->Indent();
|
|
if (ServerOnlyStreaming(method)) {
|
|
printer->Print(vars, "m := new($Request$)\n");
|
|
vars["Error_Check"] = "err := stream.RecvMsg(m); err != nil";
|
|
GenerateError(printer, vars, false);
|
|
printer->Print(
|
|
vars,
|
|
"return srv.($Service$Server).$Method$(m, &$StreamType${stream})\n");
|
|
} else {
|
|
printer->Print(
|
|
vars, "return srv.($Service$Server).$Method$(&$StreamType${stream})\n");
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
bool genSend = method->BidiStreaming() || ServerOnlyStreaming(method);
|
|
bool genRecv = method->BidiStreaming() || ClientOnlyStreaming(method);
|
|
bool genSendAndClose = ClientOnlyStreaming(method);
|
|
|
|
printer->Print(vars, "type $Service$_$Method$Server interface {\n");
|
|
printer->Indent();
|
|
if (genSend) { printer->Print(vars, "Send(*$Response$) error\n"); }
|
|
if (genRecv) { printer->Print(vars, "Recv() (*$Request$, error)\n"); }
|
|
if (genSendAndClose) {
|
|
printer->Print(vars, "SendAndClose(*$Response$) error\n");
|
|
}
|
|
printer->Print(vars, "$grpc$.ServerStream\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
printer->Print(vars, "type $StreamType$ struct {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "$grpc$.ServerStream\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
if (genSend) {
|
|
printer->Print(vars,
|
|
"func (x *$StreamType$) Send(m *$Response$) error {\n");
|
|
printer->Indent();
|
|
printer->Print("return x.ServerStream.SendMsg(m)\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
if (genRecv) {
|
|
printer->Print(vars,
|
|
"func (x *$StreamType$) Recv() (*$Request$, error) {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "m := new($Request$)\n");
|
|
vars["Error_Check"] = "err := x.ServerStream.RecvMsg(m); err != nil";
|
|
GenerateError(printer, vars);
|
|
printer->Print("return m, nil\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
if (genSendAndClose) {
|
|
printer->Print(
|
|
vars, "func (x *$StreamType$) SendAndClose(m *$Response$) error {\n");
|
|
printer->Indent();
|
|
printer->Print("return x.ServerStream.SendMsg(m)\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
}
|
|
|
|
// Generates Client method signature source
|
|
void GenerateClientMethodSignature(const grpc_generator::Method *method,
|
|
grpc_generator::Printer *printer,
|
|
std::map<grpc::string, grpc::string> vars) {
|
|
vars["Method"] = exportName(method->name());
|
|
vars["Request"] =
|
|
", in *" + ((vars["CustomMethodIO"] == "") ? method->get_input_type_name()
|
|
: vars["CustomMethodIO"]);
|
|
if (ClientOnlyStreaming(method) || method->BidiStreaming()) {
|
|
vars["Request"] = "";
|
|
}
|
|
vars["Response"] = "*" + method->get_output_type_name();
|
|
if (ClientOnlyStreaming(method) || method->BidiStreaming() ||
|
|
ServerOnlyStreaming(method)) {
|
|
vars["Response"] = vars["Service"] + "_" + vars["Method"] + "Client";
|
|
}
|
|
printer->Print(vars,
|
|
"$Method$(ctx $context$.Context$Request$,\n\topts "
|
|
"...$grpc$.CallOption) ($Response$, error)$Ending$");
|
|
}
|
|
|
|
// Generates Client method source
|
|
void GenerateClientMethod(const grpc_generator::Method *method,
|
|
grpc_generator::Printer *printer,
|
|
std::map<grpc::string, grpc::string> vars) {
|
|
printer->Print(vars, "func (c *$ServiceUnexported$Client) ");
|
|
vars["Ending"] = " {\n";
|
|
GenerateClientMethodSignature(method, printer, vars);
|
|
printer->Indent();
|
|
vars["Method"] = exportName(method->name());
|
|
vars["Request"] = (vars["CustomMethodIO"] == "")
|
|
? method->get_input_type_name()
|
|
: vars["CustomMethodIO"];
|
|
vars["Response"] = method->get_output_type_name();
|
|
vars["FullMethodName"] =
|
|
"/" + vars["ServicePrefix"] + vars["Service"] + "/" + vars["Method"];
|
|
if (method->NoStreaming()) {
|
|
printer->Print(vars, "out := new($Response$)\n");
|
|
printer->Print(
|
|
vars,
|
|
"err := c.cc.Invoke(ctx, \"$FullMethodName$\", in, out, opts...)\n");
|
|
vars["Error_Check"] = "err != nil";
|
|
GenerateError(printer, vars);
|
|
printer->Print("return out, nil\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
return;
|
|
}
|
|
vars["StreamType"] = vars["ServiceUnexported"] + vars["Method"] + "Client";
|
|
printer->Print(vars,
|
|
"stream, err := c.cc.NewStream(ctx, &$MethodDesc$, "
|
|
"\"$FullMethodName$\", opts...)\n");
|
|
vars["Error_Check"] = "err != nil";
|
|
GenerateError(printer, vars);
|
|
|
|
printer->Print(vars, "x := &$StreamType${stream}\n");
|
|
if (ServerOnlyStreaming(method)) {
|
|
vars["Error_Check"] = "err := x.ClientStream.SendMsg(in); err != nil";
|
|
GenerateError(printer, vars);
|
|
vars["Error_Check"] = "err := x.ClientStream.CloseSend(); err != nil";
|
|
GenerateError(printer, vars);
|
|
}
|
|
printer->Print("return x, nil\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
bool genSend = method->BidiStreaming() || ClientOnlyStreaming(method);
|
|
bool genRecv = method->BidiStreaming() || ServerOnlyStreaming(method);
|
|
bool genCloseAndRecv = ClientOnlyStreaming(method);
|
|
|
|
// Stream interface
|
|
printer->Print(vars, "type $Service$_$Method$Client interface {\n");
|
|
printer->Indent();
|
|
if (genSend) { printer->Print(vars, "Send(*$Request$) error\n"); }
|
|
if (genRecv) { printer->Print(vars, "Recv() (*$Response$, error)\n"); }
|
|
if (genCloseAndRecv) {
|
|
printer->Print(vars, "CloseAndRecv() (*$Response$, error)\n");
|
|
}
|
|
printer->Print(vars, "$grpc$.ClientStream\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
// Stream Client
|
|
printer->Print(vars, "type $StreamType$ struct {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "$grpc$.ClientStream\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
if (genSend) {
|
|
printer->Print(vars, "func (x *$StreamType$) Send(m *$Request$) error {\n");
|
|
printer->Indent();
|
|
printer->Print("return x.ClientStream.SendMsg(m)\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
if (genRecv) {
|
|
printer->Print(vars,
|
|
"func (x *$StreamType$) Recv() (*$Response$, error) {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "m := new($Response$)\n");
|
|
vars["Error_Check"] = "err := x.ClientStream.RecvMsg(m); err != nil";
|
|
GenerateError(printer, vars);
|
|
printer->Print("return m, nil\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
|
|
if (genCloseAndRecv) {
|
|
printer->Print(
|
|
vars, "func (x *$StreamType$) CloseAndRecv() (*$Response$, error) {\n");
|
|
printer->Indent();
|
|
vars["Error_Check"] = "err := x.ClientStream.CloseSend(); err != nil";
|
|
GenerateError(printer, vars);
|
|
printer->Print(vars, "m := new($Response$)\n");
|
|
vars["Error_Check"] = "err := x.ClientStream.RecvMsg(m); err != nil";
|
|
GenerateError(printer, vars);
|
|
printer->Print("return m, nil\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
}
|
|
}
|
|
|
|
// Generates client API for the service
|
|
void GenerateService(const grpc_generator::Service *service,
|
|
grpc_generator::Printer *printer,
|
|
std::map<grpc::string, grpc::string> vars) {
|
|
vars["Service"] = exportName(service->name());
|
|
// Client Interface
|
|
printer->Print(vars, "// Client API for $Service$ service\n");
|
|
printer->Print(vars, "type $Service$Client interface {\n");
|
|
printer->Indent();
|
|
vars["Ending"] = "\n";
|
|
for (int i = 0; i < service->method_count(); i++) {
|
|
GenerateClientMethodSignature(service->method(i).get(), printer, vars);
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
// Client structure
|
|
vars["ServiceUnexported"] = unexportName(vars["Service"]);
|
|
printer->Print(vars, "type $ServiceUnexported$Client struct {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "cc $grpc$.ClientConnInterface\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
// NewClient
|
|
printer->Print(vars,
|
|
"func New$Service$Client(cc $grpc$.ClientConnInterface) "
|
|
"$Service$Client {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "return &$ServiceUnexported$Client{cc}");
|
|
printer->Outdent();
|
|
printer->Print("\n}\n\n");
|
|
|
|
int unary_methods = 0, streaming_methods = 0;
|
|
vars["ServiceDesc"] = "_" + vars["Service"] + "_serviceDesc";
|
|
for (int i = 0; i < service->method_count(); i++) {
|
|
auto method = service->method(i);
|
|
if (method->NoStreaming()) {
|
|
vars["MethodDesc"] =
|
|
vars["ServiceDesc"] + ".Method[" + as_string(unary_methods) + "]";
|
|
unary_methods++;
|
|
} else {
|
|
vars["MethodDesc"] = vars["ServiceDesc"] + ".Streams[" +
|
|
as_string(streaming_methods) + "]";
|
|
streaming_methods++;
|
|
}
|
|
GenerateClientMethod(method.get(), printer, vars);
|
|
}
|
|
|
|
// Server Interface
|
|
printer->Print(vars, "// Server API for $Service$ service\n");
|
|
printer->Print(vars, "type $Service$Server interface {\n");
|
|
printer->Indent();
|
|
vars["Ending"] = "\n";
|
|
for (int i = 0; i < service->method_count(); i++) {
|
|
GenerateServerMethodSignature(service->method(i).get(), printer, vars);
|
|
}
|
|
printer->Print(vars, "mustEmbedUnimplemented$Service$Server()\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
printer->Print(vars, "type Unimplemented$Service$Server struct {\n");
|
|
printer->Print("}\n\n");
|
|
|
|
vars["Ending"] = " {\n";
|
|
for (int i = 0; i < service->method_count(); i++) {
|
|
auto method = service->method(i);
|
|
vars["Method"] = exportName(method->name());
|
|
vars["Nil"] = method->NoStreaming() ? "nil, " : "";
|
|
printer->Print(vars, "func (Unimplemented$Service$Server) ");
|
|
GenerateServerMethodSignature(method.get(), printer, vars);
|
|
printer->Indent();
|
|
printer->Print(vars,
|
|
"return $Nil$status.Errorf(codes.Unimplemented, \"method "
|
|
"$Method$ not implemented\")\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
printer->Print("\n");
|
|
}
|
|
|
|
printer->Print(vars,
|
|
"func (Unimplemented$Service$Server) "
|
|
"mustEmbedUnimplemented$Service$Server() {}");
|
|
printer->Print("\n\n");
|
|
|
|
printer->Print(vars, "type Unsafe$Service$Server interface {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "mustEmbedUnimplemented$Service$Server()\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
// Server registration.
|
|
printer->Print(vars,
|
|
"func Register$Service$Server(s $grpc$.ServiceRegistrar, srv "
|
|
"$Service$Server) {\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "s.RegisterService(&$ServiceDesc$, srv)\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n\n");
|
|
|
|
for (int i = 0; i < service->method_count(); i++) {
|
|
GenerateServerMethod(service->method(i).get(), printer, vars);
|
|
}
|
|
|
|
// Service Descriptor
|
|
printer->Print(vars, "var $ServiceDesc$ = $grpc$.ServiceDesc{\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "ServiceName: \"$ServicePrefix$$Service$\",\n");
|
|
printer->Print(vars, "HandlerType: (*$Service$Server)(nil),\n");
|
|
printer->Print(vars, "Methods: []$grpc$.MethodDesc{\n");
|
|
printer->Indent();
|
|
for (int i = 0; i < service->method_count(); i++) {
|
|
auto method = service->method(i);
|
|
vars["Method"] = exportName(method->name());
|
|
vars["Handler"] = "_" + vars["Service"] + "_" + vars["Method"] + "_Handler";
|
|
if (method->NoStreaming()) {
|
|
printer->Print("{\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "MethodName: \"$Method$\",\n");
|
|
printer->Print(vars, "Handler: $Handler$,\n");
|
|
printer->Outdent();
|
|
printer->Print("},\n");
|
|
}
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("},\n");
|
|
printer->Print(vars, "Streams: []$grpc$.StreamDesc{\n");
|
|
printer->Indent();
|
|
for (int i = 0; i < service->method_count(); i++) {
|
|
auto method = service->method(i);
|
|
vars["Method"] = exportName(method->name());
|
|
vars["Handler"] = "_" + vars["Service"] + "_" + vars["Method"] + "_Handler";
|
|
if (!method->NoStreaming()) {
|
|
printer->Print("{\n");
|
|
printer->Indent();
|
|
printer->Print(vars, "StreamName: \"$Method$\",\n");
|
|
printer->Print(vars, "Handler: $Handler$,\n");
|
|
if (ClientOnlyStreaming(method.get())) {
|
|
printer->Print("ClientStreams: true,\n");
|
|
} else if (ServerOnlyStreaming(method.get())) {
|
|
printer->Print("ServerStreams: true,\n");
|
|
} else {
|
|
printer->Print("ServerStreams: true,\n");
|
|
printer->Print("ClientStreams: true,\n");
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("},\n");
|
|
}
|
|
}
|
|
printer->Outdent();
|
|
printer->Print("},\n");
|
|
printer->Outdent();
|
|
printer->Print("}\n");
|
|
}
|
|
|
|
// Returns source for the service
|
|
grpc::string GenerateServiceSource(grpc_generator::File *file,
|
|
const grpc_generator::Service *service,
|
|
grpc_go_generator::Parameters *parameters) {
|
|
grpc::string out;
|
|
auto p = file->CreatePrinter(&out, '\t');
|
|
p->SetIndentationSize(1);
|
|
auto printer = p.get();
|
|
std::map<grpc::string, grpc::string> vars;
|
|
vars["Package"] = parameters->package_name;
|
|
vars["ServicePrefix"] = parameters->service_prefix;
|
|
if (!parameters->service_prefix.empty()) vars["ServicePrefix"].append(".");
|
|
vars["grpc"] = "grpc";
|
|
vars["context"] = "context";
|
|
GenerateImports(file, printer, vars);
|
|
if (parameters->custom_method_io_type != "") {
|
|
vars["CustomMethodIO"] = parameters->custom_method_io_type;
|
|
}
|
|
GenerateService(service, printer, vars);
|
|
return out;
|
|
}
|
|
} // Namespace grpc_go_generator
|