The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
https://grpc.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
763 lines
27 KiB
763 lines
27 KiB
/* |
|
* |
|
* Copyright 2015 gRPC authors. |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
* |
|
*/ |
|
|
|
#include <cctype> |
|
#include <map> |
|
#include <sstream> |
|
#include <vector> |
|
|
|
#include "src/compiler/config.h" |
|
#include "src/compiler/csharp_generator.h" |
|
#include "src/compiler/csharp_generator_helpers.h" |
|
|
|
using google::protobuf::compiler::csharp::GetClassName; |
|
using google::protobuf::compiler::csharp::GetFileNamespace; |
|
using google::protobuf::compiler::csharp::GetReflectionClassName; |
|
using grpc::protobuf::Descriptor; |
|
using grpc::protobuf::FileDescriptor; |
|
using grpc::protobuf::MethodDescriptor; |
|
using grpc::protobuf::ServiceDescriptor; |
|
using grpc::protobuf::io::Printer; |
|
using grpc::protobuf::io::StringOutputStream; |
|
using grpc_generator::GetMethodType; |
|
using grpc_generator::MethodType; |
|
using grpc_generator::METHODTYPE_BIDI_STREAMING; |
|
using grpc_generator::METHODTYPE_CLIENT_STREAMING; |
|
using grpc_generator::METHODTYPE_NO_STREAMING; |
|
using grpc_generator::METHODTYPE_SERVER_STREAMING; |
|
using grpc_generator::StringReplace; |
|
using std::map; |
|
using std::vector; |
|
|
|
namespace grpc_csharp_generator { |
|
namespace { |
|
|
|
// This function is a massaged version of |
|
// https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc |
|
// Currently, we cannot easily reuse the functionality as |
|
// google/protobuf/compiler/csharp/csharp_doc_comment.h is not a public header. |
|
// TODO(jtattermusch): reuse the functionality from google/protobuf. |
|
bool GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer, |
|
grpc::protobuf::SourceLocation location) { |
|
grpc::string comments = location.leading_comments.empty() |
|
? location.trailing_comments |
|
: location.leading_comments; |
|
if (comments.empty()) { |
|
return false; |
|
} |
|
// XML escaping... no need for apostrophes etc as the whole text is going to |
|
// be a child |
|
// node of a summary element, not part of an attribute. |
|
comments = grpc_generator::StringReplace(comments, "&", "&", true); |
|
comments = grpc_generator::StringReplace(comments, "<", "<", true); |
|
|
|
std::vector<grpc::string> lines; |
|
grpc_generator::Split(comments, '\n', &lines); |
|
// TODO: We really should work out which part to put in the summary and which |
|
// to put in the remarks... |
|
// but that needs to be part of a bigger effort to understand the markdown |
|
// better anyway. |
|
printer->Print("/// <summary>\n"); |
|
bool last_was_empty = false; |
|
// We squash multiple blank lines down to one, and remove any trailing blank |
|
// lines. We need |
|
// to preserve the blank lines themselves, as this is relevant in the |
|
// markdown. |
|
// Note that we can't remove leading or trailing whitespace as *that's* |
|
// relevant in markdown too. |
|
// (We don't skip "just whitespace" lines, either.) |
|
for (std::vector<grpc::string>::iterator it = lines.begin(); |
|
it != lines.end(); ++it) { |
|
grpc::string line = *it; |
|
if (line.empty()) { |
|
last_was_empty = true; |
|
} else { |
|
if (last_was_empty) { |
|
printer->Print("///\n"); |
|
} |
|
last_was_empty = false; |
|
printer->Print("///$line$\n", "line", *it); |
|
} |
|
} |
|
printer->Print("/// </summary>\n"); |
|
return true; |
|
} |
|
|
|
template <typename DescriptorType> |
|
bool GenerateDocCommentBody(grpc::protobuf::io::Printer* printer, |
|
const DescriptorType* descriptor) { |
|
grpc::protobuf::SourceLocation location; |
|
if (!descriptor->GetSourceLocation(&location)) { |
|
return false; |
|
} |
|
return GenerateDocCommentBodyImpl(printer, location); |
|
} |
|
|
|
void GenerateDocCommentServerMethod(grpc::protobuf::io::Printer* printer, |
|
const MethodDescriptor* method) { |
|
if (GenerateDocCommentBody(printer, method)) { |
|
if (method->client_streaming()) { |
|
printer->Print( |
|
"/// <param name=\"requestStream\">Used for reading requests from " |
|
"the client.</param>\n"); |
|
} else { |
|
printer->Print( |
|
"/// <param name=\"request\">The request received from the " |
|
"client.</param>\n"); |
|
} |
|
if (method->server_streaming()) { |
|
printer->Print( |
|
"/// <param name=\"responseStream\">Used for sending responses back " |
|
"to the client.</param>\n"); |
|
} |
|
printer->Print( |
|
"/// <param name=\"context\">The context of the server-side call " |
|
"handler being invoked.</param>\n"); |
|
if (method->server_streaming()) { |
|
printer->Print( |
|
"/// <returns>A task indicating completion of the " |
|
"handler.</returns>\n"); |
|
} else { |
|
printer->Print( |
|
"/// <returns>The response to send back to the client (wrapped by a " |
|
"task).</returns>\n"); |
|
} |
|
} |
|
} |
|
|
|
void GenerateDocCommentClientMethod(grpc::protobuf::io::Printer* printer, |
|
const MethodDescriptor* method, |
|
bool is_sync, bool use_call_options) { |
|
if (GenerateDocCommentBody(printer, method)) { |
|
if (!method->client_streaming()) { |
|
printer->Print( |
|
"/// <param name=\"request\">The request to send to the " |
|
"server.</param>\n"); |
|
} |
|
if (!use_call_options) { |
|
printer->Print( |
|
"/// <param name=\"headers\">The initial metadata to send with the " |
|
"call. This parameter is optional.</param>\n"); |
|
printer->Print( |
|
"/// <param name=\"deadline\">An optional deadline for the call. The " |
|
"call will be cancelled if deadline is hit.</param>\n"); |
|
printer->Print( |
|
"/// <param name=\"cancellationToken\">An optional token for " |
|
"canceling the call.</param>\n"); |
|
} else { |
|
printer->Print( |
|
"/// <param name=\"options\">The options for the call.</param>\n"); |
|
} |
|
if (is_sync) { |
|
printer->Print( |
|
"/// <returns>The response received from the server.</returns>\n"); |
|
} else { |
|
printer->Print("/// <returns>The call object.</returns>\n"); |
|
} |
|
} |
|
} |
|
|
|
std::string GetServiceClassName(const ServiceDescriptor* service) { |
|
return service->name(); |
|
} |
|
|
|
std::string GetClientClassName(const ServiceDescriptor* service) { |
|
return service->name() + "Client"; |
|
} |
|
|
|
std::string GetServerClassName(const ServiceDescriptor* service) { |
|
return service->name() + "Base"; |
|
} |
|
|
|
std::string GetCSharpMethodType(MethodType method_type) { |
|
switch (method_type) { |
|
case METHODTYPE_NO_STREAMING: |
|
return "grpc::MethodType.Unary"; |
|
case METHODTYPE_CLIENT_STREAMING: |
|
return "grpc::MethodType.ClientStreaming"; |
|
case METHODTYPE_SERVER_STREAMING: |
|
return "grpc::MethodType.ServerStreaming"; |
|
case METHODTYPE_BIDI_STREAMING: |
|
return "grpc::MethodType.DuplexStreaming"; |
|
} |
|
GOOGLE_LOG(FATAL) << "Can't get here."; |
|
return ""; |
|
} |
|
|
|
std::string GetCSharpServerMethodType(MethodType method_type) { |
|
switch (method_type) { |
|
case METHODTYPE_NO_STREAMING: |
|
return "grpc::UnaryServerMethod"; |
|
case METHODTYPE_CLIENT_STREAMING: |
|
return "grpc::ClientStreamingServerMethod"; |
|
case METHODTYPE_SERVER_STREAMING: |
|
return "grpc::ServerStreamingServerMethod"; |
|
case METHODTYPE_BIDI_STREAMING: |
|
return "grpc::DuplexStreamingServerMethod"; |
|
} |
|
GOOGLE_LOG(FATAL) << "Can't get here."; |
|
return ""; |
|
} |
|
|
|
std::string GetServiceNameFieldName() { return "__ServiceName"; } |
|
|
|
std::string GetMarshallerFieldName(const Descriptor* message) { |
|
return "__Marshaller_" + |
|
grpc_generator::StringReplace(message->full_name(), ".", "_", true); |
|
} |
|
|
|
std::string GetMethodFieldName(const MethodDescriptor* method) { |
|
return "__Method_" + method->name(); |
|
} |
|
|
|
std::string GetMethodRequestParamMaybe(const MethodDescriptor* method, |
|
bool invocation_param = false) { |
|
if (method->client_streaming()) { |
|
return ""; |
|
} |
|
if (invocation_param) { |
|
return "request, "; |
|
} |
|
return GetClassName(method->input_type()) + " request, "; |
|
} |
|
|
|
std::string GetAccessLevel(bool internal_access) { |
|
return internal_access ? "internal" : "public"; |
|
} |
|
|
|
std::string GetMethodReturnTypeClient(const MethodDescriptor* method) { |
|
switch (GetMethodType(method)) { |
|
case METHODTYPE_NO_STREAMING: |
|
return "grpc::AsyncUnaryCall<" + GetClassName(method->output_type()) + |
|
">"; |
|
case METHODTYPE_CLIENT_STREAMING: |
|
return "grpc::AsyncClientStreamingCall<" + |
|
GetClassName(method->input_type()) + ", " + |
|
GetClassName(method->output_type()) + ">"; |
|
case METHODTYPE_SERVER_STREAMING: |
|
return "grpc::AsyncServerStreamingCall<" + |
|
GetClassName(method->output_type()) + ">"; |
|
case METHODTYPE_BIDI_STREAMING: |
|
return "grpc::AsyncDuplexStreamingCall<" + |
|
GetClassName(method->input_type()) + ", " + |
|
GetClassName(method->output_type()) + ">"; |
|
} |
|
GOOGLE_LOG(FATAL) << "Can't get here."; |
|
return ""; |
|
} |
|
|
|
std::string GetMethodRequestParamServer(const MethodDescriptor* method) { |
|
switch (GetMethodType(method)) { |
|
case METHODTYPE_NO_STREAMING: |
|
case METHODTYPE_SERVER_STREAMING: |
|
return GetClassName(method->input_type()) + " request"; |
|
case METHODTYPE_CLIENT_STREAMING: |
|
case METHODTYPE_BIDI_STREAMING: |
|
return "grpc::IAsyncStreamReader<" + GetClassName(method->input_type()) + |
|
"> requestStream"; |
|
} |
|
GOOGLE_LOG(FATAL) << "Can't get here."; |
|
return ""; |
|
} |
|
|
|
std::string GetMethodReturnTypeServer(const MethodDescriptor* method) { |
|
switch (GetMethodType(method)) { |
|
case METHODTYPE_NO_STREAMING: |
|
case METHODTYPE_CLIENT_STREAMING: |
|
return "global::System.Threading.Tasks.Task<" + |
|
GetClassName(method->output_type()) + ">"; |
|
case METHODTYPE_SERVER_STREAMING: |
|
case METHODTYPE_BIDI_STREAMING: |
|
return "global::System.Threading.Tasks.Task"; |
|
} |
|
GOOGLE_LOG(FATAL) << "Can't get here."; |
|
return ""; |
|
} |
|
|
|
std::string GetMethodResponseStreamMaybe(const MethodDescriptor* method) { |
|
switch (GetMethodType(method)) { |
|
case METHODTYPE_NO_STREAMING: |
|
case METHODTYPE_CLIENT_STREAMING: |
|
return ""; |
|
case METHODTYPE_SERVER_STREAMING: |
|
case METHODTYPE_BIDI_STREAMING: |
|
return ", grpc::IServerStreamWriter<" + |
|
GetClassName(method->output_type()) + "> responseStream"; |
|
} |
|
GOOGLE_LOG(FATAL) << "Can't get here."; |
|
return ""; |
|
} |
|
|
|
// Gets vector of all messages used as input or output types. |
|
std::vector<const Descriptor*> GetUsedMessages( |
|
const ServiceDescriptor* service) { |
|
std::set<const Descriptor*> descriptor_set; |
|
std::vector<const Descriptor*> |
|
result; // vector is to maintain stable ordering |
|
for (int i = 0; i < service->method_count(); i++) { |
|
const MethodDescriptor* method = service->method(i); |
|
if (descriptor_set.find(method->input_type()) == descriptor_set.end()) { |
|
descriptor_set.insert(method->input_type()); |
|
result.push_back(method->input_type()); |
|
} |
|
if (descriptor_set.find(method->output_type()) == descriptor_set.end()) { |
|
descriptor_set.insert(method->output_type()); |
|
result.push_back(method->output_type()); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) { |
|
std::vector<const Descriptor*> used_messages = GetUsedMessages(service); |
|
for (size_t i = 0; i < used_messages.size(); i++) { |
|
const Descriptor* message = used_messages[i]; |
|
out->Print( |
|
"static readonly grpc::Marshaller<$type$> $fieldname$ = " |
|
"grpc::Marshallers.Create((arg) => " |
|
"global::Google.Protobuf.MessageExtensions.ToByteArray(arg), " |
|
"$type$.Parser.ParseFrom);\n", |
|
"fieldname", GetMarshallerFieldName(message), "type", |
|
GetClassName(message)); |
|
} |
|
out->Print("\n"); |
|
} |
|
|
|
void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) { |
|
out->Print( |
|
"static readonly grpc::Method<$request$, $response$> $fieldname$ = new " |
|
"grpc::Method<$request$, $response$>(\n", |
|
"fieldname", GetMethodFieldName(method), "request", |
|
GetClassName(method->input_type()), "response", |
|
GetClassName(method->output_type())); |
|
out->Indent(); |
|
out->Indent(); |
|
out->Print("$methodtype$,\n", "methodtype", |
|
GetCSharpMethodType(GetMethodType(method))); |
|
out->Print("$servicenamefield$,\n", "servicenamefield", |
|
GetServiceNameFieldName()); |
|
out->Print("\"$methodname$\",\n", "methodname", method->name()); |
|
out->Print("$requestmarshaller$,\n", "requestmarshaller", |
|
GetMarshallerFieldName(method->input_type())); |
|
out->Print("$responsemarshaller$);\n", "responsemarshaller", |
|
GetMarshallerFieldName(method->output_type())); |
|
out->Print("\n"); |
|
out->Outdent(); |
|
out->Outdent(); |
|
} |
|
|
|
void GenerateServiceDescriptorProperty(Printer* out, |
|
const ServiceDescriptor* service) { |
|
std::ostringstream index; |
|
index << service->index(); |
|
out->Print("/// <summary>Service descriptor</summary>\n"); |
|
out->Print( |
|
"public static global::Google.Protobuf.Reflection.ServiceDescriptor " |
|
"Descriptor\n"); |
|
out->Print("{\n"); |
|
out->Print(" get { return $umbrella$.Descriptor.Services[$index$]; }\n", |
|
"umbrella", GetReflectionClassName(service->file()), "index", |
|
index.str()); |
|
out->Print("}\n"); |
|
out->Print("\n"); |
|
} |
|
|
|
void GenerateServerClass(Printer* out, const ServiceDescriptor* service) { |
|
out->Print( |
|
"/// <summary>Base class for server-side implementations of " |
|
"$servicename$</summary>\n", |
|
"servicename", GetServiceClassName(service)); |
|
out->Print( |
|
"[grpc::BindServiceMethod(typeof($classname$), " |
|
"\"BindService\")]\n", |
|
"classname", GetServiceClassName(service)); |
|
out->Print("public abstract partial class $name$\n", "name", |
|
GetServerClassName(service)); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
for (int i = 0; i < service->method_count(); i++) { |
|
const MethodDescriptor* method = service->method(i); |
|
GenerateDocCommentServerMethod(out, method); |
|
out->Print( |
|
"public virtual $returntype$ " |
|
"$methodname$($request$$response_stream_maybe$, " |
|
"grpc::ServerCallContext context)\n", |
|
"methodname", method->name(), "returntype", |
|
GetMethodReturnTypeServer(method), "request", |
|
GetMethodRequestParamServer(method), "response_stream_maybe", |
|
GetMethodResponseStreamMaybe(method)); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
out->Print( |
|
"throw new grpc::RpcException(" |
|
"new grpc::Status(grpc::StatusCode.Unimplemented, \"\"));\n"); |
|
out->Outdent(); |
|
out->Print("}\n\n"); |
|
} |
|
out->Outdent(); |
|
out->Print("}\n"); |
|
out->Print("\n"); |
|
} |
|
|
|
void GenerateClientStub(Printer* out, const ServiceDescriptor* service) { |
|
out->Print("/// <summary>Client for $servicename$</summary>\n", "servicename", |
|
GetServiceClassName(service)); |
|
out->Print("public partial class $name$ : grpc::ClientBase<$name$>\n", "name", |
|
GetClientClassName(service)); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
|
|
// constructors |
|
out->Print( |
|
"/// <summary>Creates a new client for $servicename$</summary>\n" |
|
"/// <param name=\"channel\">The channel to use to make remote " |
|
"calls.</param>\n", |
|
"servicename", GetServiceClassName(service)); |
|
out->Print("public $name$(grpc::ChannelBase channel) : base(channel)\n", |
|
"name", GetClientClassName(service)); |
|
out->Print("{\n"); |
|
out->Print("}\n"); |
|
out->Print( |
|
"/// <summary>Creates a new client for $servicename$ that uses a custom " |
|
"<c>CallInvoker</c>.</summary>\n" |
|
"/// <param name=\"callInvoker\">The callInvoker to use to make remote " |
|
"calls.</param>\n", |
|
"servicename", GetServiceClassName(service)); |
|
out->Print( |
|
"public $name$(grpc::CallInvoker callInvoker) : base(callInvoker)\n", |
|
"name", GetClientClassName(service)); |
|
out->Print("{\n"); |
|
out->Print("}\n"); |
|
out->Print( |
|
"/// <summary>Protected parameterless constructor to allow creation" |
|
" of test doubles.</summary>\n"); |
|
out->Print("protected $name$() : base()\n", "name", |
|
GetClientClassName(service)); |
|
out->Print("{\n"); |
|
out->Print("}\n"); |
|
out->Print( |
|
"/// <summary>Protected constructor to allow creation of configured " |
|
"clients.</summary>\n" |
|
"/// <param name=\"configuration\">The client configuration.</param>\n"); |
|
out->Print( |
|
"protected $name$(ClientBaseConfiguration configuration)" |
|
" : base(configuration)\n", |
|
"name", GetClientClassName(service)); |
|
out->Print("{\n"); |
|
out->Print("}\n\n"); |
|
|
|
for (int i = 0; i < service->method_count(); i++) { |
|
const MethodDescriptor* method = service->method(i); |
|
MethodType method_type = GetMethodType(method); |
|
|
|
if (method_type == METHODTYPE_NO_STREAMING) { |
|
// unary calls have an extra synchronous stub method |
|
GenerateDocCommentClientMethod(out, method, true, false); |
|
out->Print( |
|
"public virtual $response$ $methodname$($request$ request, " |
|
"grpc::Metadata " |
|
"headers = null, global::System.DateTime? deadline = null, " |
|
"global::System.Threading.CancellationToken " |
|
"cancellationToken = " |
|
"default(global::System.Threading.CancellationToken))\n", |
|
"methodname", method->name(), "request", |
|
GetClassName(method->input_type()), "response", |
|
GetClassName(method->output_type())); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
out->Print( |
|
"return $methodname$(request, new grpc::CallOptions(headers, " |
|
"deadline, " |
|
"cancellationToken));\n", |
|
"methodname", method->name()); |
|
out->Outdent(); |
|
out->Print("}\n"); |
|
|
|
// overload taking CallOptions as a param |
|
GenerateDocCommentClientMethod(out, method, true, true); |
|
out->Print( |
|
"public virtual $response$ $methodname$($request$ request, " |
|
"grpc::CallOptions options)\n", |
|
"methodname", method->name(), "request", |
|
GetClassName(method->input_type()), "response", |
|
GetClassName(method->output_type())); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
out->Print( |
|
"return CallInvoker.BlockingUnaryCall($methodfield$, null, options, " |
|
"request);\n", |
|
"methodfield", GetMethodFieldName(method)); |
|
out->Outdent(); |
|
out->Print("}\n"); |
|
} |
|
|
|
std::string method_name = method->name(); |
|
if (method_type == METHODTYPE_NO_STREAMING) { |
|
method_name += "Async"; // prevent name clash with synchronous method. |
|
} |
|
GenerateDocCommentClientMethod(out, method, false, false); |
|
out->Print( |
|
"public virtual $returntype$ " |
|
"$methodname$($request_maybe$grpc::Metadata " |
|
"headers = null, global::System.DateTime? deadline = null, " |
|
"global::System.Threading.CancellationToken " |
|
"cancellationToken = " |
|
"default(global::System.Threading.CancellationToken))\n", |
|
"methodname", method_name, "request_maybe", |
|
GetMethodRequestParamMaybe(method), "returntype", |
|
GetMethodReturnTypeClient(method)); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
|
|
out->Print( |
|
"return $methodname$($request_maybe$new grpc::CallOptions(headers, " |
|
"deadline, " |
|
"cancellationToken));\n", |
|
"methodname", method_name, "request_maybe", |
|
GetMethodRequestParamMaybe(method, true)); |
|
out->Outdent(); |
|
out->Print("}\n"); |
|
|
|
// overload taking CallOptions as a param |
|
GenerateDocCommentClientMethod(out, method, false, true); |
|
out->Print( |
|
"public virtual $returntype$ " |
|
"$methodname$($request_maybe$grpc::CallOptions " |
|
"options)\n", |
|
"methodname", method_name, "request_maybe", |
|
GetMethodRequestParamMaybe(method), "returntype", |
|
GetMethodReturnTypeClient(method)); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
switch (GetMethodType(method)) { |
|
case METHODTYPE_NO_STREAMING: |
|
out->Print( |
|
"return CallInvoker.AsyncUnaryCall($methodfield$, null, options, " |
|
"request);\n", |
|
"methodfield", GetMethodFieldName(method)); |
|
break; |
|
case METHODTYPE_CLIENT_STREAMING: |
|
out->Print( |
|
"return CallInvoker.AsyncClientStreamingCall($methodfield$, null, " |
|
"options);\n", |
|
"methodfield", GetMethodFieldName(method)); |
|
break; |
|
case METHODTYPE_SERVER_STREAMING: |
|
out->Print( |
|
"return CallInvoker.AsyncServerStreamingCall($methodfield$, null, " |
|
"options, request);\n", |
|
"methodfield", GetMethodFieldName(method)); |
|
break; |
|
case METHODTYPE_BIDI_STREAMING: |
|
out->Print( |
|
"return CallInvoker.AsyncDuplexStreamingCall($methodfield$, null, " |
|
"options);\n", |
|
"methodfield", GetMethodFieldName(method)); |
|
break; |
|
default: |
|
GOOGLE_LOG(FATAL) << "Can't get here."; |
|
} |
|
out->Outdent(); |
|
out->Print("}\n"); |
|
} |
|
|
|
// override NewInstance method |
|
out->Print( |
|
"/// <summary>Creates a new instance of client from given " |
|
"<c>ClientBaseConfiguration</c>.</summary>\n"); |
|
out->Print( |
|
"protected override $name$ NewInstance(ClientBaseConfiguration " |
|
"configuration)\n", |
|
"name", GetClientClassName(service)); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
out->Print("return new $name$(configuration);\n", "name", |
|
GetClientClassName(service)); |
|
out->Outdent(); |
|
out->Print("}\n"); |
|
|
|
out->Outdent(); |
|
out->Print("}\n"); |
|
out->Print("\n"); |
|
} |
|
|
|
void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor* service) { |
|
out->Print( |
|
"/// <summary>Creates service definition that can be registered with a " |
|
"server</summary>\n"); |
|
out->Print( |
|
"/// <param name=\"serviceImpl\">An object implementing the server-side" |
|
" handling logic.</param>\n"); |
|
out->Print( |
|
"public static grpc::ServerServiceDefinition BindService($implclass$ " |
|
"serviceImpl)\n", |
|
"implclass", GetServerClassName(service)); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
|
|
out->Print("return grpc::ServerServiceDefinition.CreateBuilder()"); |
|
out->Indent(); |
|
out->Indent(); |
|
for (int i = 0; i < service->method_count(); i++) { |
|
const MethodDescriptor* method = service->method(i); |
|
out->Print("\n.AddMethod($methodfield$, serviceImpl.$methodname$)", |
|
"methodfield", GetMethodFieldName(method), "methodname", |
|
method->name()); |
|
} |
|
out->Print(".Build();\n"); |
|
out->Outdent(); |
|
out->Outdent(); |
|
|
|
out->Outdent(); |
|
out->Print("}\n"); |
|
out->Print("\n"); |
|
} |
|
|
|
void GenerateBindServiceWithBinderMethod(Printer* out, |
|
const ServiceDescriptor* service) { |
|
out->Print( |
|
"/// <summary>Register service method with a service " |
|
"binder with or without implementation. Useful when customizing the " |
|
"service binding logic.\n" |
|
"/// Note: this method is part of an experimental API that can change or " |
|
"be " |
|
"removed without any prior notice.</summary>\n"); |
|
out->Print( |
|
"/// <param name=\"serviceBinder\">Service methods will be bound by " |
|
"calling <c>AddMethod</c> on this object." |
|
"</param>\n"); |
|
out->Print( |
|
"/// <param name=\"serviceImpl\">An object implementing the server-side" |
|
" handling logic.</param>\n"); |
|
out->Print( |
|
"public static void BindService(grpc::ServiceBinderBase serviceBinder, " |
|
"$implclass$ " |
|
"serviceImpl)\n", |
|
"implclass", GetServerClassName(service)); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
|
|
for (int i = 0; i < service->method_count(); i++) { |
|
const MethodDescriptor* method = service->method(i); |
|
out->Print( |
|
"serviceBinder.AddMethod($methodfield$, serviceImpl == null ? null : " |
|
"new $servermethodtype$<$inputtype$, $outputtype$>(" |
|
"serviceImpl.$methodname$));\n", |
|
"methodfield", GetMethodFieldName(method), "servermethodtype", |
|
GetCSharpServerMethodType(GetMethodType(method)), "inputtype", |
|
GetClassName(method->input_type()), "outputtype", |
|
GetClassName(method->output_type()), "methodname", method->name()); |
|
} |
|
|
|
out->Outdent(); |
|
out->Print("}\n"); |
|
out->Print("\n"); |
|
} |
|
|
|
void GenerateService(Printer* out, const ServiceDescriptor* service, |
|
bool generate_client, bool generate_server, |
|
bool internal_access) { |
|
GenerateDocCommentBody(out, service); |
|
out->Print("$access_level$ static partial class $classname$\n", |
|
"access_level", GetAccessLevel(internal_access), "classname", |
|
GetServiceClassName(service)); |
|
out->Print("{\n"); |
|
out->Indent(); |
|
out->Print("static readonly string $servicenamefield$ = \"$servicename$\";\n", |
|
"servicenamefield", GetServiceNameFieldName(), "servicename", |
|
service->full_name()); |
|
out->Print("\n"); |
|
|
|
GenerateMarshallerFields(out, service); |
|
for (int i = 0; i < service->method_count(); i++) { |
|
GenerateStaticMethodField(out, service->method(i)); |
|
} |
|
GenerateServiceDescriptorProperty(out, service); |
|
|
|
if (generate_server) { |
|
GenerateServerClass(out, service); |
|
} |
|
if (generate_client) { |
|
GenerateClientStub(out, service); |
|
} |
|
if (generate_server) { |
|
GenerateBindServiceMethod(out, service); |
|
GenerateBindServiceWithBinderMethod(out, service); |
|
} |
|
|
|
out->Outdent(); |
|
out->Print("}\n"); |
|
} |
|
|
|
} // anonymous namespace |
|
|
|
grpc::string GetServices(const FileDescriptor* file, bool generate_client, |
|
bool generate_server, bool internal_access) { |
|
grpc::string output; |
|
{ |
|
// Scope the output stream so it closes and finalizes output to the string. |
|
|
|
StringOutputStream output_stream(&output); |
|
Printer out(&output_stream, '$'); |
|
|
|
// Don't write out any output if there no services, to avoid empty service |
|
// files being generated for proto files that don't declare any. |
|
if (file->service_count() == 0) { |
|
return output; |
|
} |
|
|
|
// Write out a file header. |
|
out.Print("// <auto-generated>\n"); |
|
out.Print( |
|
"// Generated by the protocol buffer compiler. DO NOT EDIT!\n"); |
|
out.Print("// source: $filename$\n", "filename", file->name()); |
|
out.Print("// </auto-generated>\n"); |
|
|
|
// use C++ style as there are no file-level XML comments in .NET |
|
grpc::string leading_comments = GetCsharpComments(file, true); |
|
if (!leading_comments.empty()) { |
|
out.Print("// Original file comments:\n"); |
|
out.PrintRaw(leading_comments.c_str()); |
|
} |
|
|
|
out.Print("#pragma warning disable 0414, 1591\n"); |
|
|
|
out.Print("#region Designer generated code\n"); |
|
out.Print("\n"); |
|
out.Print("using grpc = global::Grpc.Core;\n"); |
|
out.Print("\n"); |
|
|
|
grpc::string file_namespace = GetFileNamespace(file); |
|
if (file_namespace != "") { |
|
out.Print("namespace $namespace$ {\n", "namespace", file_namespace); |
|
out.Indent(); |
|
} |
|
for (int i = 0; i < file->service_count(); i++) { |
|
GenerateService(&out, file->service(i), generate_client, generate_server, |
|
internal_access); |
|
} |
|
if (file_namespace != "") { |
|
out.Outdent(); |
|
out.Print("}\n"); |
|
} |
|
out.Print("#endregion\n"); |
|
} |
|
return output; |
|
} |
|
|
|
} // namespace grpc_csharp_generator
|
|
|