diff options
| author | Devtools Arcadia <[email protected]> | 2022-02-07 18:08:42 +0300 |
|---|---|---|
| committer | Devtools Arcadia <[email protected]> | 2022-02-07 18:08:42 +0300 |
| commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
| tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/libs/grpc/src/compiler/csharp_generator.cc | |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/libs/grpc/src/compiler/csharp_generator.cc')
| -rw-r--r-- | contrib/libs/grpc/src/compiler/csharp_generator.cc | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/contrib/libs/grpc/src/compiler/csharp_generator.cc b/contrib/libs/grpc/src/compiler/csharp_generator.cc new file mode 100644 index 00000000000..3b7107c4c48 --- /dev/null +++ b/contrib/libs/grpc/src/compiler/csharp_generator.cc @@ -0,0 +1,826 @@ +/* + * + * 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) { + TString 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<TString> 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<TString>::iterator it = lines.begin(); it != lines.end(); + ++it) { + TString 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"); + } + } +} + +TString GetServiceClassName(const ServiceDescriptor* service) { + return service->name(); +} + +TString GetClientClassName(const ServiceDescriptor* service) { + return service->name() + "Client"; +} + +TString GetServerClassName(const ServiceDescriptor* service) { + return service->name() + "Base"; +} + +TString 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 ""; +} + +TString 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 ""; +} + +TString GetServiceNameFieldName() { return "__ServiceName"; } + +TString GetMarshallerFieldName(const Descriptor* message) { + return "__Marshaller_" + + grpc_generator::StringReplace(message->full_name(), ".", "_", true); +} + +TString GetMethodFieldName(const MethodDescriptor* method) { + return "__Method_" + method->name(); +} + +TString GetMethodRequestParamMaybe(const MethodDescriptor* method, + bool invocation_param = false) { + if (method->client_streaming()) { + return ""; + } + if (invocation_param) { + return "request, "; + } + return GetClassName(method->input_type()) + " request, "; +} + +TString GetAccessLevel(bool internal_access) { + return internal_access ? "internal" : "public"; +} + +TString 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 ""; +} + +TString 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 ""; +} + +TString 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 ""; +} + +TString 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); + if (used_messages.size() != 0) { + // Generate static helper methods for serialization/deserialization + out->Print( + "static void __Helper_SerializeMessage(" + "global::Google.Protobuf.IMessage message, " + "grpc::SerializationContext context)\n" + "{\n"); + out->Indent(); + out->Print( + "#if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION\n" + "if (message is global::Google.Protobuf.IBufferMessage)\n" + "{\n"); + out->Indent(); + out->Print( + "context.SetPayloadLength(message.CalculateSize());\n" + "global::Google.Protobuf.MessageExtensions.WriteTo(message, " + "context.GetBufferWriter());\n" + "context.Complete();\n" + "return;\n"); + out->Outdent(); + out->Print( + "}\n" + "#endif\n"); + out->Print( + "context.Complete(" + "global::Google.Protobuf.MessageExtensions.ToByteArray(message));\n"); + out->Outdent(); + out->Print("}\n\n"); + + out->Print( + "static class __Helper_MessageCache<T>\n" + "{\n"); + out->Indent(); + out->Print( + "public static readonly bool IsBufferMessage = " + "global::System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(" + "global::Google.Protobuf.IBufferMessage)).IsAssignableFrom(typeof(T));" + "\n"); + out->Outdent(); + out->Print("}\n\n"); + + out->Print( + "static T __Helper_DeserializeMessage<T>(" + "grpc::DeserializationContext context, " + "global::Google.Protobuf.MessageParser<T> parser) " + "where T : global::Google.Protobuf.IMessage<T>\n" + "{\n"); + out->Indent(); + out->Print( + "#if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION\n" + "if (__Helper_MessageCache<T>.IsBufferMessage)\n" + "{\n"); + out->Indent(); + out->Print( + "return parser.ParseFrom(context.PayloadAsReadOnlySequence());\n"); + out->Outdent(); + out->Print( + "}\n" + "#endif\n"); + out->Print("return parser.ParseFrom(context.PayloadAsNewBuffer());\n"); + out->Outdent(); + out->Print("}\n\n"); + } + + 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(__Helper_SerializeMessage, " + "context => __Helper_DeserializeMessage(context, $type$.Parser));\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"); + } + + TString 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 + +TString GetServices(const FileDescriptor* file, bool generate_client, + bool generate_server, bool internal_access) { + TString 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 + TString 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"); + + TString 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 |
