1 /*
2 *
3 * Copyright 2015 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include <cctype>
20 #include <map>
21 #include <sstream>
22 #include <vector>
23
24 #include "src/compiler/config.h"
25 #include "src/compiler/csharp_generator.h"
26 #include "src/compiler/csharp_generator_helpers.h"
27
28 using google::protobuf::compiler::csharp::GetClassName;
29 using google::protobuf::compiler::csharp::GetFileNamespace;
30 using google::protobuf::compiler::csharp::GetReflectionClassName;
31 using grpc::protobuf::Descriptor;
32 using grpc::protobuf::FileDescriptor;
33 using grpc::protobuf::MethodDescriptor;
34 using grpc::protobuf::ServiceDescriptor;
35 using grpc::protobuf::io::Printer;
36 using grpc::protobuf::io::StringOutputStream;
37 using grpc_generator::GetMethodType;
38 using grpc_generator::METHODTYPE_BIDI_STREAMING;
39 using grpc_generator::METHODTYPE_CLIENT_STREAMING;
40 using grpc_generator::METHODTYPE_NO_STREAMING;
41 using grpc_generator::METHODTYPE_SERVER_STREAMING;
42 using grpc_generator::MethodType;
43 using grpc_generator::StringReplace;
44 using std::map;
45 using std::vector;
46
47 namespace grpc_csharp_generator {
48 namespace {
49
50 // This function is a massaged version of
51 // https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc
52 // Currently, we cannot easily reuse the functionality as
53 // google/protobuf/compiler/csharp/csharp_doc_comment.h is not a public header.
54 // TODO(jtattermusch): reuse the functionality from google/protobuf.
GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer * printer,grpc::protobuf::SourceLocation location)55 bool GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer,
56 grpc::protobuf::SourceLocation location) {
57 grpc::string comments = location.leading_comments.empty()
58 ? location.trailing_comments
59 : location.leading_comments;
60 if (comments.empty()) {
61 return false;
62 }
63 // XML escaping... no need for apostrophes etc as the whole text is going to
64 // be a child
65 // node of a summary element, not part of an attribute.
66 comments = grpc_generator::StringReplace(comments, "&", "&", true);
67 comments = grpc_generator::StringReplace(comments, "<", "<", true);
68
69 std::vector<grpc::string> lines;
70 grpc_generator::Split(comments, '\n', &lines);
71 // TODO: We really should work out which part to put in the summary and which
72 // to put in the remarks...
73 // but that needs to be part of a bigger effort to understand the markdown
74 // better anyway.
75 printer->Print("/// <summary>\n");
76 bool last_was_empty = false;
77 // We squash multiple blank lines down to one, and remove any trailing blank
78 // lines. We need
79 // to preserve the blank lines themselves, as this is relevant in the
80 // markdown.
81 // Note that we can't remove leading or trailing whitespace as *that's*
82 // relevant in markdown too.
83 // (We don't skip "just whitespace" lines, either.)
84 for (std::vector<grpc::string>::iterator it = lines.begin();
85 it != lines.end(); ++it) {
86 grpc::string line = *it;
87 if (line.empty()) {
88 last_was_empty = true;
89 } else {
90 if (last_was_empty) {
91 printer->Print("///\n");
92 }
93 last_was_empty = false;
94 printer->Print("///$line$\n", "line", *it);
95 }
96 }
97 printer->Print("/// </summary>\n");
98 return true;
99 }
100
101 template <typename DescriptorType>
GenerateDocCommentBody(grpc::protobuf::io::Printer * printer,const DescriptorType * descriptor)102 bool GenerateDocCommentBody(grpc::protobuf::io::Printer* printer,
103 const DescriptorType* descriptor) {
104 grpc::protobuf::SourceLocation location;
105 if (!descriptor->GetSourceLocation(&location)) {
106 return false;
107 }
108 return GenerateDocCommentBodyImpl(printer, location);
109 }
110
GenerateDocCommentServerMethod(grpc::protobuf::io::Printer * printer,const MethodDescriptor * method)111 void GenerateDocCommentServerMethod(grpc::protobuf::io::Printer* printer,
112 const MethodDescriptor* method) {
113 if (GenerateDocCommentBody(printer, method)) {
114 if (method->client_streaming()) {
115 printer->Print(
116 "/// <param name=\"requestStream\">Used for reading requests from "
117 "the client.</param>\n");
118 } else {
119 printer->Print(
120 "/// <param name=\"request\">The request received from the "
121 "client.</param>\n");
122 }
123 if (method->server_streaming()) {
124 printer->Print(
125 "/// <param name=\"responseStream\">Used for sending responses back "
126 "to the client.</param>\n");
127 }
128 printer->Print(
129 "/// <param name=\"context\">The context of the server-side call "
130 "handler being invoked.</param>\n");
131 if (method->server_streaming()) {
132 printer->Print(
133 "/// <returns>A task indicating completion of the "
134 "handler.</returns>\n");
135 } else {
136 printer->Print(
137 "/// <returns>The response to send back to the client (wrapped by a "
138 "task).</returns>\n");
139 }
140 }
141 }
142
GenerateDocCommentClientMethod(grpc::protobuf::io::Printer * printer,const MethodDescriptor * method,bool is_sync,bool use_call_options)143 void GenerateDocCommentClientMethod(grpc::protobuf::io::Printer* printer,
144 const MethodDescriptor* method,
145 bool is_sync, bool use_call_options) {
146 if (GenerateDocCommentBody(printer, method)) {
147 if (!method->client_streaming()) {
148 printer->Print(
149 "/// <param name=\"request\">The request to send to the "
150 "server.</param>\n");
151 }
152 if (!use_call_options) {
153 printer->Print(
154 "/// <param name=\"headers\">The initial metadata to send with the "
155 "call. This parameter is optional.</param>\n");
156 printer->Print(
157 "/// <param name=\"deadline\">An optional deadline for the call. The "
158 "call will be cancelled if deadline is hit.</param>\n");
159 printer->Print(
160 "/// <param name=\"cancellationToken\">An optional token for "
161 "canceling the call.</param>\n");
162 } else {
163 printer->Print(
164 "/// <param name=\"options\">The options for the call.</param>\n");
165 }
166 if (is_sync) {
167 printer->Print(
168 "/// <returns>The response received from the server.</returns>\n");
169 } else {
170 printer->Print("/// <returns>The call object.</returns>\n");
171 }
172 }
173 }
174
GetServiceClassName(const ServiceDescriptor * service)175 std::string GetServiceClassName(const ServiceDescriptor* service) {
176 return service->name();
177 }
178
GetClientClassName(const ServiceDescriptor * service)179 std::string GetClientClassName(const ServiceDescriptor* service) {
180 return service->name() + "Client";
181 }
182
GetServerClassName(const ServiceDescriptor * service)183 std::string GetServerClassName(const ServiceDescriptor* service) {
184 return service->name() + "Base";
185 }
186
GetCSharpMethodType(MethodType method_type)187 std::string GetCSharpMethodType(MethodType method_type) {
188 switch (method_type) {
189 case METHODTYPE_NO_STREAMING:
190 return "grpc::MethodType.Unary";
191 case METHODTYPE_CLIENT_STREAMING:
192 return "grpc::MethodType.ClientStreaming";
193 case METHODTYPE_SERVER_STREAMING:
194 return "grpc::MethodType.ServerStreaming";
195 case METHODTYPE_BIDI_STREAMING:
196 return "grpc::MethodType.DuplexStreaming";
197 }
198 GOOGLE_LOG(FATAL) << "Can't get here.";
199 return "";
200 }
201
GetServiceNameFieldName()202 std::string GetServiceNameFieldName() { return "__ServiceName"; }
203
GetMarshallerFieldName(const Descriptor * message)204 std::string GetMarshallerFieldName(const Descriptor* message) {
205 return "__Marshaller_" +
206 grpc_generator::StringReplace(message->full_name(), ".", "_", true);
207 }
208
GetMethodFieldName(const MethodDescriptor * method)209 std::string GetMethodFieldName(const MethodDescriptor* method) {
210 return "__Method_" + method->name();
211 }
212
GetMethodRequestParamMaybe(const MethodDescriptor * method,bool invocation_param=false)213 std::string GetMethodRequestParamMaybe(const MethodDescriptor* method,
214 bool invocation_param = false) {
215 if (method->client_streaming()) {
216 return "";
217 }
218 if (invocation_param) {
219 return "request, ";
220 }
221 return GetClassName(method->input_type()) + " request, ";
222 }
223
GetAccessLevel(bool internal_access)224 std::string GetAccessLevel(bool internal_access) {
225 return internal_access ? "internal" : "public";
226 }
227
GetMethodReturnTypeClient(const MethodDescriptor * method)228 std::string GetMethodReturnTypeClient(const MethodDescriptor* method) {
229 switch (GetMethodType(method)) {
230 case METHODTYPE_NO_STREAMING:
231 return "grpc::AsyncUnaryCall<" + GetClassName(method->output_type()) +
232 ">";
233 case METHODTYPE_CLIENT_STREAMING:
234 return "grpc::AsyncClientStreamingCall<" +
235 GetClassName(method->input_type()) + ", " +
236 GetClassName(method->output_type()) + ">";
237 case METHODTYPE_SERVER_STREAMING:
238 return "grpc::AsyncServerStreamingCall<" +
239 GetClassName(method->output_type()) + ">";
240 case METHODTYPE_BIDI_STREAMING:
241 return "grpc::AsyncDuplexStreamingCall<" +
242 GetClassName(method->input_type()) + ", " +
243 GetClassName(method->output_type()) + ">";
244 }
245 GOOGLE_LOG(FATAL) << "Can't get here.";
246 return "";
247 }
248
GetMethodRequestParamServer(const MethodDescriptor * method)249 std::string GetMethodRequestParamServer(const MethodDescriptor* method) {
250 switch (GetMethodType(method)) {
251 case METHODTYPE_NO_STREAMING:
252 case METHODTYPE_SERVER_STREAMING:
253 return GetClassName(method->input_type()) + " request";
254 case METHODTYPE_CLIENT_STREAMING:
255 case METHODTYPE_BIDI_STREAMING:
256 return "grpc::IAsyncStreamReader<" + GetClassName(method->input_type()) +
257 "> requestStream";
258 }
259 GOOGLE_LOG(FATAL) << "Can't get here.";
260 return "";
261 }
262
GetMethodReturnTypeServer(const MethodDescriptor * method)263 std::string GetMethodReturnTypeServer(const MethodDescriptor* method) {
264 switch (GetMethodType(method)) {
265 case METHODTYPE_NO_STREAMING:
266 case METHODTYPE_CLIENT_STREAMING:
267 return "global::System.Threading.Tasks.Task<" +
268 GetClassName(method->output_type()) + ">";
269 case METHODTYPE_SERVER_STREAMING:
270 case METHODTYPE_BIDI_STREAMING:
271 return "global::System.Threading.Tasks.Task";
272 }
273 GOOGLE_LOG(FATAL) << "Can't get here.";
274 return "";
275 }
276
GetMethodResponseStreamMaybe(const MethodDescriptor * method)277 std::string GetMethodResponseStreamMaybe(const MethodDescriptor* method) {
278 switch (GetMethodType(method)) {
279 case METHODTYPE_NO_STREAMING:
280 case METHODTYPE_CLIENT_STREAMING:
281 return "";
282 case METHODTYPE_SERVER_STREAMING:
283 case METHODTYPE_BIDI_STREAMING:
284 return ", grpc::IServerStreamWriter<" +
285 GetClassName(method->output_type()) + "> responseStream";
286 }
287 GOOGLE_LOG(FATAL) << "Can't get here.";
288 return "";
289 }
290
291 // Gets vector of all messages used as input or output types.
GetUsedMessages(const ServiceDescriptor * service)292 std::vector<const Descriptor*> GetUsedMessages(
293 const ServiceDescriptor* service) {
294 std::set<const Descriptor*> descriptor_set;
295 std::vector<const Descriptor*>
296 result; // vector is to maintain stable ordering
297 for (int i = 0; i < service->method_count(); i++) {
298 const MethodDescriptor* method = service->method(i);
299 if (descriptor_set.find(method->input_type()) == descriptor_set.end()) {
300 descriptor_set.insert(method->input_type());
301 result.push_back(method->input_type());
302 }
303 if (descriptor_set.find(method->output_type()) == descriptor_set.end()) {
304 descriptor_set.insert(method->output_type());
305 result.push_back(method->output_type());
306 }
307 }
308 return result;
309 }
310
GenerateMarshallerFields(Printer * out,const ServiceDescriptor * service)311 void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) {
312 std::vector<const Descriptor*> used_messages = GetUsedMessages(service);
313 for (size_t i = 0; i < used_messages.size(); i++) {
314 const Descriptor* message = used_messages[i];
315 out->Print(
316 "static readonly grpc::Marshaller<$type$> $fieldname$ = "
317 "grpc::Marshallers.Create((arg) => "
318 "global::Google.Protobuf.MessageExtensions.ToByteArray(arg), "
319 "$type$.Parser.ParseFrom);\n",
320 "fieldname", GetMarshallerFieldName(message), "type",
321 GetClassName(message));
322 }
323 out->Print("\n");
324 }
325
GenerateStaticMethodField(Printer * out,const MethodDescriptor * method)326 void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) {
327 out->Print(
328 "static readonly grpc::Method<$request$, $response$> $fieldname$ = new "
329 "grpc::Method<$request$, $response$>(\n",
330 "fieldname", GetMethodFieldName(method), "request",
331 GetClassName(method->input_type()), "response",
332 GetClassName(method->output_type()));
333 out->Indent();
334 out->Indent();
335 out->Print("$methodtype$,\n", "methodtype",
336 GetCSharpMethodType(GetMethodType(method)));
337 out->Print("$servicenamefield$,\n", "servicenamefield",
338 GetServiceNameFieldName());
339 out->Print("\"$methodname$\",\n", "methodname", method->name());
340 out->Print("$requestmarshaller$,\n", "requestmarshaller",
341 GetMarshallerFieldName(method->input_type()));
342 out->Print("$responsemarshaller$);\n", "responsemarshaller",
343 GetMarshallerFieldName(method->output_type()));
344 out->Print("\n");
345 out->Outdent();
346 out->Outdent();
347 }
348
GenerateServiceDescriptorProperty(Printer * out,const ServiceDescriptor * service)349 void GenerateServiceDescriptorProperty(Printer* out,
350 const ServiceDescriptor* service) {
351 std::ostringstream index;
352 index << service->index();
353 out->Print("/// <summary>Service descriptor</summary>\n");
354 out->Print(
355 "public static global::Google.Protobuf.Reflection.ServiceDescriptor "
356 "Descriptor\n");
357 out->Print("{\n");
358 out->Print(" get { return $umbrella$.Descriptor.Services[$index$]; }\n",
359 "umbrella", GetReflectionClassName(service->file()), "index",
360 index.str());
361 out->Print("}\n");
362 out->Print("\n");
363 }
364
GenerateServerClass(Printer * out,const ServiceDescriptor * service)365 void GenerateServerClass(Printer* out, const ServiceDescriptor* service) {
366 out->Print(
367 "/// <summary>Base class for server-side implementations of "
368 "$servicename$</summary>\n",
369 "servicename", GetServiceClassName(service));
370 out->Print("public abstract partial class $name$\n", "name",
371 GetServerClassName(service));
372 out->Print("{\n");
373 out->Indent();
374 for (int i = 0; i < service->method_count(); i++) {
375 const MethodDescriptor* method = service->method(i);
376 GenerateDocCommentServerMethod(out, method);
377 out->Print(
378 "public virtual $returntype$ "
379 "$methodname$($request$$response_stream_maybe$, "
380 "grpc::ServerCallContext context)\n",
381 "methodname", method->name(), "returntype",
382 GetMethodReturnTypeServer(method), "request",
383 GetMethodRequestParamServer(method), "response_stream_maybe",
384 GetMethodResponseStreamMaybe(method));
385 out->Print("{\n");
386 out->Indent();
387 out->Print(
388 "throw new grpc::RpcException("
389 "new grpc::Status(grpc::StatusCode.Unimplemented, \"\"));\n");
390 out->Outdent();
391 out->Print("}\n\n");
392 }
393 out->Outdent();
394 out->Print("}\n");
395 out->Print("\n");
396 }
397
GenerateClientStub(Printer * out,const ServiceDescriptor * service)398 void GenerateClientStub(Printer* out, const ServiceDescriptor* service) {
399 out->Print("/// <summary>Client for $servicename$</summary>\n", "servicename",
400 GetServiceClassName(service));
401 out->Print("public partial class $name$ : grpc::ClientBase<$name$>\n", "name",
402 GetClientClassName(service));
403 out->Print("{\n");
404 out->Indent();
405
406 // constructors
407 out->Print(
408 "/// <summary>Creates a new client for $servicename$</summary>\n"
409 "/// <param name=\"channel\">The channel to use to make remote "
410 "calls.</param>\n",
411 "servicename", GetServiceClassName(service));
412 out->Print("public $name$(grpc::Channel channel) : base(channel)\n", "name",
413 GetClientClassName(service));
414 out->Print("{\n");
415 out->Print("}\n");
416 out->Print(
417 "/// <summary>Creates a new client for $servicename$ that uses a custom "
418 "<c>CallInvoker</c>.</summary>\n"
419 "/// <param name=\"callInvoker\">The callInvoker to use to make remote "
420 "calls.</param>\n",
421 "servicename", GetServiceClassName(service));
422 out->Print(
423 "public $name$(grpc::CallInvoker callInvoker) : base(callInvoker)\n",
424 "name", GetClientClassName(service));
425 out->Print("{\n");
426 out->Print("}\n");
427 out->Print(
428 "/// <summary>Protected parameterless constructor to allow creation"
429 " of test doubles.</summary>\n");
430 out->Print("protected $name$() : base()\n", "name",
431 GetClientClassName(service));
432 out->Print("{\n");
433 out->Print("}\n");
434 out->Print(
435 "/// <summary>Protected constructor to allow creation of configured "
436 "clients.</summary>\n"
437 "/// <param name=\"configuration\">The client configuration.</param>\n");
438 out->Print(
439 "protected $name$(ClientBaseConfiguration configuration)"
440 " : base(configuration)\n",
441 "name", GetClientClassName(service));
442 out->Print("{\n");
443 out->Print("}\n\n");
444
445 for (int i = 0; i < service->method_count(); i++) {
446 const MethodDescriptor* method = service->method(i);
447 MethodType method_type = GetMethodType(method);
448
449 if (method_type == METHODTYPE_NO_STREAMING) {
450 // unary calls have an extra synchronous stub method
451 GenerateDocCommentClientMethod(out, method, true, false);
452 out->Print(
453 "public virtual $response$ $methodname$($request$ request, "
454 "grpc::Metadata "
455 "headers = null, global::System.DateTime? deadline = null, "
456 "global::System.Threading.CancellationToken "
457 "cancellationToken = "
458 "default(global::System.Threading.CancellationToken))\n",
459 "methodname", method->name(), "request",
460 GetClassName(method->input_type()), "response",
461 GetClassName(method->output_type()));
462 out->Print("{\n");
463 out->Indent();
464 out->Print(
465 "return $methodname$(request, new grpc::CallOptions(headers, "
466 "deadline, "
467 "cancellationToken));\n",
468 "methodname", method->name());
469 out->Outdent();
470 out->Print("}\n");
471
472 // overload taking CallOptions as a param
473 GenerateDocCommentClientMethod(out, method, true, true);
474 out->Print(
475 "public virtual $response$ $methodname$($request$ request, "
476 "grpc::CallOptions options)\n",
477 "methodname", method->name(), "request",
478 GetClassName(method->input_type()), "response",
479 GetClassName(method->output_type()));
480 out->Print("{\n");
481 out->Indent();
482 out->Print(
483 "return CallInvoker.BlockingUnaryCall($methodfield$, null, options, "
484 "request);\n",
485 "methodfield", GetMethodFieldName(method));
486 out->Outdent();
487 out->Print("}\n");
488 }
489
490 std::string method_name = method->name();
491 if (method_type == METHODTYPE_NO_STREAMING) {
492 method_name += "Async"; // prevent name clash with synchronous method.
493 }
494 GenerateDocCommentClientMethod(out, method, false, false);
495 out->Print(
496 "public virtual $returntype$ "
497 "$methodname$($request_maybe$grpc::Metadata "
498 "headers = null, global::System.DateTime? deadline = null, "
499 "global::System.Threading.CancellationToken "
500 "cancellationToken = "
501 "default(global::System.Threading.CancellationToken))\n",
502 "methodname", method_name, "request_maybe",
503 GetMethodRequestParamMaybe(method), "returntype",
504 GetMethodReturnTypeClient(method));
505 out->Print("{\n");
506 out->Indent();
507
508 out->Print(
509 "return $methodname$($request_maybe$new grpc::CallOptions(headers, "
510 "deadline, "
511 "cancellationToken));\n",
512 "methodname", method_name, "request_maybe",
513 GetMethodRequestParamMaybe(method, true));
514 out->Outdent();
515 out->Print("}\n");
516
517 // overload taking CallOptions as a param
518 GenerateDocCommentClientMethod(out, method, false, true);
519 out->Print(
520 "public virtual $returntype$ "
521 "$methodname$($request_maybe$grpc::CallOptions "
522 "options)\n",
523 "methodname", method_name, "request_maybe",
524 GetMethodRequestParamMaybe(method), "returntype",
525 GetMethodReturnTypeClient(method));
526 out->Print("{\n");
527 out->Indent();
528 switch (GetMethodType(method)) {
529 case METHODTYPE_NO_STREAMING:
530 out->Print(
531 "return CallInvoker.AsyncUnaryCall($methodfield$, null, options, "
532 "request);\n",
533 "methodfield", GetMethodFieldName(method));
534 break;
535 case METHODTYPE_CLIENT_STREAMING:
536 out->Print(
537 "return CallInvoker.AsyncClientStreamingCall($methodfield$, null, "
538 "options);\n",
539 "methodfield", GetMethodFieldName(method));
540 break;
541 case METHODTYPE_SERVER_STREAMING:
542 out->Print(
543 "return CallInvoker.AsyncServerStreamingCall($methodfield$, null, "
544 "options, request);\n",
545 "methodfield", GetMethodFieldName(method));
546 break;
547 case METHODTYPE_BIDI_STREAMING:
548 out->Print(
549 "return CallInvoker.AsyncDuplexStreamingCall($methodfield$, null, "
550 "options);\n",
551 "methodfield", GetMethodFieldName(method));
552 break;
553 default:
554 GOOGLE_LOG(FATAL) << "Can't get here.";
555 }
556 out->Outdent();
557 out->Print("}\n");
558 }
559
560 // override NewInstance method
561 out->Print(
562 "/// <summary>Creates a new instance of client from given "
563 "<c>ClientBaseConfiguration</c>.</summary>\n");
564 out->Print(
565 "protected override $name$ NewInstance(ClientBaseConfiguration "
566 "configuration)\n",
567 "name", GetClientClassName(service));
568 out->Print("{\n");
569 out->Indent();
570 out->Print("return new $name$(configuration);\n", "name",
571 GetClientClassName(service));
572 out->Outdent();
573 out->Print("}\n");
574
575 out->Outdent();
576 out->Print("}\n");
577 out->Print("\n");
578 }
579
GenerateBindServiceMethod(Printer * out,const ServiceDescriptor * service)580 void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor* service) {
581 out->Print(
582 "/// <summary>Creates service definition that can be registered with a "
583 "server</summary>\n");
584 out->Print(
585 "/// <param name=\"serviceImpl\">An object implementing the server-side"
586 " handling logic.</param>\n");
587 out->Print(
588 "public static grpc::ServerServiceDefinition BindService($implclass$ "
589 "serviceImpl)\n",
590 "implclass", GetServerClassName(service));
591 out->Print("{\n");
592 out->Indent();
593
594 out->Print("return grpc::ServerServiceDefinition.CreateBuilder()");
595 out->Indent();
596 out->Indent();
597 for (int i = 0; i < service->method_count(); i++) {
598 const MethodDescriptor* method = service->method(i);
599 out->Print("\n.AddMethod($methodfield$, serviceImpl.$methodname$)",
600 "methodfield", GetMethodFieldName(method), "methodname",
601 method->name());
602 }
603 out->Print(".Build();\n");
604 out->Outdent();
605 out->Outdent();
606
607 out->Outdent();
608 out->Print("}\n");
609 out->Print("\n");
610 }
611
GenerateService(Printer * out,const ServiceDescriptor * service,bool generate_client,bool generate_server,bool internal_access)612 void GenerateService(Printer* out, const ServiceDescriptor* service,
613 bool generate_client, bool generate_server,
614 bool internal_access) {
615 GenerateDocCommentBody(out, service);
616 out->Print("$access_level$ static partial class $classname$\n",
617 "access_level", GetAccessLevel(internal_access), "classname",
618 GetServiceClassName(service));
619 out->Print("{\n");
620 out->Indent();
621 out->Print("static readonly string $servicenamefield$ = \"$servicename$\";\n",
622 "servicenamefield", GetServiceNameFieldName(), "servicename",
623 service->full_name());
624 out->Print("\n");
625
626 GenerateMarshallerFields(out, service);
627 for (int i = 0; i < service->method_count(); i++) {
628 GenerateStaticMethodField(out, service->method(i));
629 }
630 GenerateServiceDescriptorProperty(out, service);
631
632 if (generate_server) {
633 GenerateServerClass(out, service);
634 }
635 if (generate_client) {
636 GenerateClientStub(out, service);
637 }
638 if (generate_server) {
639 GenerateBindServiceMethod(out, service);
640 }
641
642 out->Outdent();
643 out->Print("}\n");
644 }
645
646 } // anonymous namespace
647
GetServices(const FileDescriptor * file,bool generate_client,bool generate_server,bool internal_access)648 grpc::string GetServices(const FileDescriptor* file, bool generate_client,
649 bool generate_server, bool internal_access) {
650 grpc::string output;
651 {
652 // Scope the output stream so it closes and finalizes output to the string.
653
654 StringOutputStream output_stream(&output);
655 Printer out(&output_stream, '$');
656
657 // Don't write out any output if there no services, to avoid empty service
658 // files being generated for proto files that don't declare any.
659 if (file->service_count() == 0) {
660 return output;
661 }
662
663 // Write out a file header.
664 out.Print("// <auto-generated>\n");
665 out.Print(
666 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n");
667 out.Print("// source: $filename$\n", "filename", file->name());
668 out.Print("// </auto-generated>\n");
669
670 // use C++ style as there are no file-level XML comments in .NET
671 grpc::string leading_comments = GetCsharpComments(file, true);
672 if (!leading_comments.empty()) {
673 out.Print("// Original file comments:\n");
674 out.PrintRaw(leading_comments.c_str());
675 }
676
677 out.Print("#pragma warning disable 0414, 1591\n");
678
679 out.Print("#region Designer generated code\n");
680 out.Print("\n");
681 out.Print("using grpc = global::Grpc.Core;\n");
682 out.Print("\n");
683
684 grpc::string file_namespace = GetFileNamespace(file);
685 if (file_namespace != "") {
686 out.Print("namespace $namespace$ {\n", "namespace", file_namespace);
687 out.Indent();
688 }
689 for (int i = 0; i < file->service_count(); i++) {
690 GenerateService(&out, file->service(i), generate_client, generate_server,
691 internal_access);
692 }
693 if (file_namespace != "") {
694 out.Outdent();
695 out.Print("}\n");
696 }
697 out.Print("#endregion\n");
698 }
699 return output;
700 }
701
702 } // namespace grpc_csharp_generator
703