/* * * 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 "src/compiler/csharp_generator.h" #include #include #include #include #include "src/compiler/config.h" #include "src/compiler/csharp_generator_helpers.h" 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::StringReplace; using std::vector; namespace grpc_csharp_generator { namespace { // This function is a massaged version of // https://github.com/protocolbuffers/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) { std::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 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("/// \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::iterator it = lines.begin(); it != lines.end(); ++it) { std::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("/// \n"); return true; } void GenerateGeneratedCodeAttribute(grpc::protobuf::io::Printer* printer) { // Mark the code as generated using the [GeneratedCode] attribute. // We don't provide plugin version info in attribute the because: // * the version information is not readily available from the plugin's code. // * it would cause a lot of churn in the pre-generated code // in this repository every time the version is updated. printer->Print( "[global::System.CodeDom.Compiler.GeneratedCode(\"grpc_csharp_plugin\", " "null)]\n"); } void GenerateObsoleteAttribute(grpc::protobuf::io::Printer* printer, bool is_deprecated) { // Mark the code deprecated using the [ObsoleteAttribute] attribute. if (is_deprecated) { printer->Print("[global::System.ObsoleteAttribute]\n"); } } template 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( "/// Used for reading requests from " "the client.\n"); } else { printer->Print( "/// The request received from the " "client.\n"); } if (method->server_streaming()) { printer->Print( "/// Used for sending responses back " "to the client.\n"); } printer->Print( "/// The context of the server-side call " "handler being invoked.\n"); if (method->server_streaming()) { printer->Print( "/// A task indicating completion of the " "handler.\n"); } else { printer->Print( "/// The response to send back to the client (wrapped by a " "task).\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( "/// The request to send to the " "server.\n"); } if (!use_call_options) { printer->Print( "/// The initial metadata to send with the " "call. This parameter is optional.\n"); printer->Print( "/// An optional deadline for the call. The " "call will be cancelled if deadline is hit.\n"); printer->Print( "/// An optional token for " "canceling the call.\n"); } else { printer->Print( "/// The options for the call.\n"); } if (is_sync) { printer->Print( "/// The response received from the server.\n"); } else { printer->Print("/// The call object.\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(const MethodDescriptor* method) { if (method->client_streaming()) { if (method->server_streaming()) { return "grpc::MethodType.DuplexStreaming"; } else { return "grpc::MethodType.ClientStreaming"; } } else { if (method->server_streaming()) { return "grpc::MethodType.ServerStreaming"; } else { return "grpc::MethodType.Unary"; } } } std::string GetCSharpServerMethodType(const MethodDescriptor* method) { if (method->client_streaming()) { if (method->server_streaming()) { return "grpc::DuplexStreamingServerMethod"; } else { return "grpc::ClientStreamingServerMethod"; } } else { if (method->server_streaming()) { return "grpc::ServerStreamingServerMethod"; } else { return "grpc::UnaryServerMethod"; } } } 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 GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->input_type()) + " request, "; } std::string GetAccessLevel(bool internal_access) { return internal_access ? "internal" : "public"; } std::string GetMethodReturnTypeClient(const MethodDescriptor* method) { if (method->client_streaming()) { if (method->server_streaming()) { return "grpc::AsyncDuplexStreamingCall<" + GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->input_type()) + ", " + GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->output_type()) + ">"; } else { return "grpc::AsyncClientStreamingCall<" + GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->input_type()) + ", " + GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->output_type()) + ">"; } } else { if (method->server_streaming()) { return "grpc::AsyncServerStreamingCall<" + GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->output_type()) + ">"; } else { return "grpc::AsyncUnaryCall<" + GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->output_type()) + ">"; } } } std::string GetMethodRequestParamServer(const MethodDescriptor* method) { if (method->client_streaming()) { return "grpc::IAsyncStreamReader<" + GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->input_type()) + "> requestStream"; } return GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->input_type()) + " request"; } std::string GetMethodReturnTypeServer(const MethodDescriptor* method) { if (method->server_streaming()) { return "global::System.Threading.Tasks.Task"; } return "global::System.Threading.Tasks.Task<" + GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->output_type()) + ">"; } std::string GetMethodResponseStreamMaybe(const MethodDescriptor* method) { if (method->server_streaming()) { return ", grpc::IServerStreamWriter<" + GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->output_type()) + "> responseStream"; } return ""; } // Gets vector of all messages used as input or output types. std::vector GetUsedMessages( const ServiceDescriptor* service) { std::set descriptor_set; std::vector 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 used_messages = GetUsedMessages(service); if (used_messages.size() != 0) { // Generate static helper methods for serialization/deserialization GenerateGeneratedCodeAttribute(out); 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"); GenerateGeneratedCodeAttribute(out); out->Print( "static class __Helper_MessageCache\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"); GenerateGeneratedCodeAttribute(out); out->Print( "static T __Helper_DeserializeMessage(" "grpc::DeserializationContext context, " "global::Google.Protobuf.MessageParser parser) " "where T : global::Google.Protobuf.IMessage\n" "{\n"); out->Indent(); out->Print( "#if !GRPC_DISABLE_PROTOBUF_BUFFER_SERIALIZATION\n" "if (__Helper_MessageCache.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]; GenerateGeneratedCodeAttribute(out); out->Print( "static readonly grpc::Marshaller<$type$> $fieldname$ = " "grpc::Marshallers.Create(__Helper_SerializeMessage, " "context => __Helper_DeserializeMessage(context, $type$.Parser));\n", "fieldname", GetMarshallerFieldName(message), "type", GRPC_CUSTOM_CSHARP_GETCLASSNAME(message)); } out->Print("\n"); } void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) { GenerateGeneratedCodeAttribute(out); out->Print( "static readonly grpc::Method<$request$, $response$> $fieldname$ = new " "grpc::Method<$request$, $response$>(\n", "fieldname", GetMethodFieldName(method), "request", GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->input_type()), "response", GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->output_type())); out->Indent(); out->Indent(); out->Print("$methodtype$,\n", "methodtype", GetCSharpMethodType(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("/// Service descriptor\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", GRPC_CUSTOM_CSHARP_GETREFLECTIONCLASSNAME(service->file()), "index", index.str()); out->Print("}\n"); out->Print("\n"); } void GenerateServerClass(Printer* out, const ServiceDescriptor* service) { out->Print( "/// Base class for server-side implementations of " "$servicename$\n", "servicename", GetServiceClassName(service)); GenerateObsoleteAttribute(out, service->options().deprecated()); 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); GenerateObsoleteAttribute(out, method->options().deprecated()); GenerateGeneratedCodeAttribute(out); 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("/// Client for $servicename$\n", "servicename", GetServiceClassName(service)); GenerateObsoleteAttribute(out, service->options().deprecated()); out->Print("public partial class $name$ : grpc::ClientBase<$name$>\n", "name", GetClientClassName(service)); out->Print("{\n"); out->Indent(); // constructors out->Print( "/// Creates a new client for $servicename$\n" "/// The channel to use to make remote " "calls.\n", "servicename", GetServiceClassName(service)); GenerateGeneratedCodeAttribute(out); out->Print("public $name$(grpc::ChannelBase channel) : base(channel)\n", "name", GetClientClassName(service)); out->Print("{\n"); out->Print("}\n"); out->Print( "/// Creates a new client for $servicename$ that uses a custom " "CallInvoker.\n" "/// The callInvoker to use to make remote " "calls.\n", "servicename", GetServiceClassName(service)); GenerateGeneratedCodeAttribute(out); out->Print( "public $name$(grpc::CallInvoker callInvoker) : base(callInvoker)\n", "name", GetClientClassName(service)); out->Print("{\n"); out->Print("}\n"); out->Print( "/// Protected parameterless constructor to allow creation" " of test doubles.\n"); GenerateGeneratedCodeAttribute(out); out->Print("protected $name$() : base()\n", "name", GetClientClassName(service)); out->Print("{\n"); out->Print("}\n"); out->Print( "/// Protected constructor to allow creation of configured " "clients.\n" "/// The client configuration.\n"); GenerateGeneratedCodeAttribute(out); 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); const bool is_deprecated = method->options().deprecated(); if (!method->client_streaming() && !method->server_streaming()) { // unary calls have an extra synchronous stub method GenerateDocCommentClientMethod(out, method, true, false); GenerateObsoleteAttribute(out, is_deprecated); GenerateGeneratedCodeAttribute(out); 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", GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->input_type()), "response", GRPC_CUSTOM_CSHARP_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); GenerateObsoleteAttribute(out, is_deprecated); GenerateGeneratedCodeAttribute(out); out->Print( "public virtual $response$ $methodname$($request$ request, " "grpc::CallOptions options)\n", "methodname", method->name(), "request", GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->input_type()), "response", GRPC_CUSTOM_CSHARP_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->client_streaming() && !method->server_streaming()) { method_name += "Async"; // prevent name clash with synchronous method. } GenerateDocCommentClientMethod(out, method, false, false); GenerateObsoleteAttribute(out, is_deprecated); GenerateGeneratedCodeAttribute(out); 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); GenerateObsoleteAttribute(out, is_deprecated); GenerateGeneratedCodeAttribute(out); 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(); if (!method->client_streaming() && !method->server_streaming()) { // Non-Streaming out->Print( "return CallInvoker.AsyncUnaryCall($methodfield$, null, options, " "request);\n", "methodfield", GetMethodFieldName(method)); } else if (method->client_streaming() && !method->server_streaming()) { // Client Streaming Only out->Print( "return CallInvoker.AsyncClientStreamingCall($methodfield$, null, " "options);\n", "methodfield", GetMethodFieldName(method)); } else if (!method->client_streaming() && method->server_streaming()) { // Server Streaming Only out->Print( "return CallInvoker.AsyncServerStreamingCall($methodfield$, null, " "options, request);\n", "methodfield", GetMethodFieldName(method)); } else { // Bi-Directional Streaming out->Print( "return CallInvoker.AsyncDuplexStreamingCall($methodfield$, null, " "options);\n", "methodfield", GetMethodFieldName(method)); } out->Outdent(); out->Print("}\n"); } // override NewInstance method out->Print( "/// Creates a new instance of client from given " "ClientBaseConfiguration.\n"); GenerateGeneratedCodeAttribute(out); 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( "/// Creates service definition that can be registered with a " "server\n"); out->Print( "/// An object implementing the server-side" " handling logic.\n"); GenerateGeneratedCodeAttribute(out); 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( "/// 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.\n"); out->Print( "/// Service methods will be bound by " "calling AddMethod on this object." "\n"); out->Print( "/// An object implementing the server-side" " handling logic.\n"); GenerateGeneratedCodeAttribute(out); 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(method), "inputtype", GRPC_CUSTOM_CSHARP_GETCLASSNAME(method->input_type()), "outputtype", GRPC_CUSTOM_CSHARP_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); GenerateObsoleteAttribute(out, service->options().deprecated()); 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 std::string GetServices(const FileDescriptor* file, bool generate_client, bool generate_server, bool internal_access) { std::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("// \n"); out.Print( "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"); out.Print("// source: $filename$\n", "filename", file->name()); out.Print("// \n"); // use C++ style as there are no file-level XML comments in .NET std::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, 8981, 0612\n"); out.Print("#region Designer generated code\n"); out.Print("\n"); out.Print("using grpc = global::Grpc.Core;\n"); out.Print("\n"); std::string file_namespace = GRPC_CUSTOM_CSHARP_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