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. |
55 | bool GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer, |
56 | grpc::protobuf::SourceLocation location) { |
57 | grpc::string = 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> |
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 | |
111 | void (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 | |
143 | void (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 | |
175 | std::string GetServiceClassName(const ServiceDescriptor* service) { |
176 | return service->name(); |
177 | } |
178 | |
179 | std::string GetClientClassName(const ServiceDescriptor* service) { |
180 | return service->name() + "Client" ; |
181 | } |
182 | |
183 | std::string GetServerClassName(const ServiceDescriptor* service) { |
184 | return service->name() + "Base" ; |
185 | } |
186 | |
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 | |
202 | std::string GetCSharpServerMethodType(MethodType method_type) { |
203 | switch (method_type) { |
204 | case METHODTYPE_NO_STREAMING: |
205 | return "grpc::UnaryServerMethod" ; |
206 | case METHODTYPE_CLIENT_STREAMING: |
207 | return "grpc::ClientStreamingServerMethod" ; |
208 | case METHODTYPE_SERVER_STREAMING: |
209 | return "grpc::ServerStreamingServerMethod" ; |
210 | case METHODTYPE_BIDI_STREAMING: |
211 | return "grpc::DuplexStreamingServerMethod" ; |
212 | } |
213 | GOOGLE_LOG(FATAL) << "Can't get here." ; |
214 | return "" ; |
215 | } |
216 | |
217 | std::string GetServiceNameFieldName() { return "__ServiceName" ; } |
218 | |
219 | std::string GetMarshallerFieldName(const Descriptor* message) { |
220 | return "__Marshaller_" + |
221 | grpc_generator::StringReplace(message->full_name(), "." , "_" , true); |
222 | } |
223 | |
224 | std::string GetMethodFieldName(const MethodDescriptor* method) { |
225 | return "__Method_" + method->name(); |
226 | } |
227 | |
228 | std::string GetMethodRequestParamMaybe(const MethodDescriptor* method, |
229 | bool invocation_param = false) { |
230 | if (method->client_streaming()) { |
231 | return "" ; |
232 | } |
233 | if (invocation_param) { |
234 | return "request, " ; |
235 | } |
236 | return GetClassName(method->input_type()) + " request, " ; |
237 | } |
238 | |
239 | std::string GetAccessLevel(bool internal_access) { |
240 | return internal_access ? "internal" : "public" ; |
241 | } |
242 | |
243 | std::string GetMethodReturnTypeClient(const MethodDescriptor* method) { |
244 | switch (GetMethodType(method)) { |
245 | case METHODTYPE_NO_STREAMING: |
246 | return "grpc::AsyncUnaryCall<" + GetClassName(method->output_type()) + |
247 | ">" ; |
248 | case METHODTYPE_CLIENT_STREAMING: |
249 | return "grpc::AsyncClientStreamingCall<" + |
250 | GetClassName(method->input_type()) + ", " + |
251 | GetClassName(method->output_type()) + ">" ; |
252 | case METHODTYPE_SERVER_STREAMING: |
253 | return "grpc::AsyncServerStreamingCall<" + |
254 | GetClassName(method->output_type()) + ">" ; |
255 | case METHODTYPE_BIDI_STREAMING: |
256 | return "grpc::AsyncDuplexStreamingCall<" + |
257 | GetClassName(method->input_type()) + ", " + |
258 | GetClassName(method->output_type()) + ">" ; |
259 | } |
260 | GOOGLE_LOG(FATAL) << "Can't get here." ; |
261 | return "" ; |
262 | } |
263 | |
264 | std::string GetMethodRequestParamServer(const MethodDescriptor* method) { |
265 | switch (GetMethodType(method)) { |
266 | case METHODTYPE_NO_STREAMING: |
267 | case METHODTYPE_SERVER_STREAMING: |
268 | return GetClassName(method->input_type()) + " request" ; |
269 | case METHODTYPE_CLIENT_STREAMING: |
270 | case METHODTYPE_BIDI_STREAMING: |
271 | return "grpc::IAsyncStreamReader<" + GetClassName(method->input_type()) + |
272 | "> requestStream" ; |
273 | } |
274 | GOOGLE_LOG(FATAL) << "Can't get here." ; |
275 | return "" ; |
276 | } |
277 | |
278 | std::string GetMethodReturnTypeServer(const MethodDescriptor* method) { |
279 | switch (GetMethodType(method)) { |
280 | case METHODTYPE_NO_STREAMING: |
281 | case METHODTYPE_CLIENT_STREAMING: |
282 | return "global::System.Threading.Tasks.Task<" + |
283 | GetClassName(method->output_type()) + ">" ; |
284 | case METHODTYPE_SERVER_STREAMING: |
285 | case METHODTYPE_BIDI_STREAMING: |
286 | return "global::System.Threading.Tasks.Task" ; |
287 | } |
288 | GOOGLE_LOG(FATAL) << "Can't get here." ; |
289 | return "" ; |
290 | } |
291 | |
292 | std::string GetMethodResponseStreamMaybe(const MethodDescriptor* method) { |
293 | switch (GetMethodType(method)) { |
294 | case METHODTYPE_NO_STREAMING: |
295 | case METHODTYPE_CLIENT_STREAMING: |
296 | return "" ; |
297 | case METHODTYPE_SERVER_STREAMING: |
298 | case METHODTYPE_BIDI_STREAMING: |
299 | return ", grpc::IServerStreamWriter<" + |
300 | GetClassName(method->output_type()) + "> responseStream" ; |
301 | } |
302 | GOOGLE_LOG(FATAL) << "Can't get here." ; |
303 | return "" ; |
304 | } |
305 | |
306 | // Gets vector of all messages used as input or output types. |
307 | std::vector<const Descriptor*> GetUsedMessages( |
308 | const ServiceDescriptor* service) { |
309 | std::set<const Descriptor*> descriptor_set; |
310 | std::vector<const Descriptor*> |
311 | result; // vector is to maintain stable ordering |
312 | for (int i = 0; i < service->method_count(); i++) { |
313 | const MethodDescriptor* method = service->method(i); |
314 | if (descriptor_set.find(method->input_type()) == descriptor_set.end()) { |
315 | descriptor_set.insert(method->input_type()); |
316 | result.push_back(method->input_type()); |
317 | } |
318 | if (descriptor_set.find(method->output_type()) == descriptor_set.end()) { |
319 | descriptor_set.insert(method->output_type()); |
320 | result.push_back(method->output_type()); |
321 | } |
322 | } |
323 | return result; |
324 | } |
325 | |
326 | void GenerateMarshallerFields(Printer* out, const ServiceDescriptor* service) { |
327 | std::vector<const Descriptor*> used_messages = GetUsedMessages(service); |
328 | for (size_t i = 0; i < used_messages.size(); i++) { |
329 | const Descriptor* message = used_messages[i]; |
330 | out->Print( |
331 | "static readonly grpc::Marshaller<$type$> $fieldname$ = " |
332 | "grpc::Marshallers.Create((arg) => " |
333 | "global::Google.Protobuf.MessageExtensions.ToByteArray(arg), " |
334 | "$type$.Parser.ParseFrom);\n" , |
335 | "fieldname" , GetMarshallerFieldName(message), "type" , |
336 | GetClassName(message)); |
337 | } |
338 | out->Print("\n" ); |
339 | } |
340 | |
341 | void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) { |
342 | out->Print( |
343 | "static readonly grpc::Method<$request$, $response$> $fieldname$ = new " |
344 | "grpc::Method<$request$, $response$>(\n" , |
345 | "fieldname" , GetMethodFieldName(method), "request" , |
346 | GetClassName(method->input_type()), "response" , |
347 | GetClassName(method->output_type())); |
348 | out->Indent(); |
349 | out->Indent(); |
350 | out->Print("$methodtype$,\n" , "methodtype" , |
351 | GetCSharpMethodType(GetMethodType(method))); |
352 | out->Print("$servicenamefield$,\n" , "servicenamefield" , |
353 | GetServiceNameFieldName()); |
354 | out->Print("\"$methodname$\",\n" , "methodname" , method->name()); |
355 | out->Print("$requestmarshaller$,\n" , "requestmarshaller" , |
356 | GetMarshallerFieldName(method->input_type())); |
357 | out->Print("$responsemarshaller$);\n" , "responsemarshaller" , |
358 | GetMarshallerFieldName(method->output_type())); |
359 | out->Print("\n" ); |
360 | out->Outdent(); |
361 | out->Outdent(); |
362 | } |
363 | |
364 | void GenerateServiceDescriptorProperty(Printer* out, |
365 | const ServiceDescriptor* service) { |
366 | std::ostringstream index; |
367 | index << service->index(); |
368 | out->Print("/// <summary>Service descriptor</summary>\n" ); |
369 | out->Print( |
370 | "public static global::Google.Protobuf.Reflection.ServiceDescriptor " |
371 | "Descriptor\n" ); |
372 | out->Print("{\n" ); |
373 | out->Print(" get { return $umbrella$.Descriptor.Services[$index$]; }\n" , |
374 | "umbrella" , GetReflectionClassName(service->file()), "index" , |
375 | index.str()); |
376 | out->Print("}\n" ); |
377 | out->Print("\n" ); |
378 | } |
379 | |
380 | void GenerateServerClass(Printer* out, const ServiceDescriptor* service) { |
381 | out->Print( |
382 | "/// <summary>Base class for server-side implementations of " |
383 | "$servicename$</summary>\n" , |
384 | "servicename" , GetServiceClassName(service)); |
385 | out->Print( |
386 | "[grpc::BindServiceMethod(typeof($classname$), " |
387 | "\"BindService\")]\n" , |
388 | "classname" , GetServiceClassName(service)); |
389 | out->Print("public abstract partial class $name$\n" , "name" , |
390 | GetServerClassName(service)); |
391 | out->Print("{\n" ); |
392 | out->Indent(); |
393 | for (int i = 0; i < service->method_count(); i++) { |
394 | const MethodDescriptor* method = service->method(i); |
395 | GenerateDocCommentServerMethod(out, method); |
396 | out->Print( |
397 | "public virtual $returntype$ " |
398 | "$methodname$($request$$response_stream_maybe$, " |
399 | "grpc::ServerCallContext context)\n" , |
400 | "methodname" , method->name(), "returntype" , |
401 | GetMethodReturnTypeServer(method), "request" , |
402 | GetMethodRequestParamServer(method), "response_stream_maybe" , |
403 | GetMethodResponseStreamMaybe(method)); |
404 | out->Print("{\n" ); |
405 | out->Indent(); |
406 | out->Print( |
407 | "throw new grpc::RpcException(" |
408 | "new grpc::Status(grpc::StatusCode.Unimplemented, \"\"));\n" ); |
409 | out->Outdent(); |
410 | out->Print("}\n\n" ); |
411 | } |
412 | out->Outdent(); |
413 | out->Print("}\n" ); |
414 | out->Print("\n" ); |
415 | } |
416 | |
417 | void GenerateClientStub(Printer* out, const ServiceDescriptor* service) { |
418 | out->Print("/// <summary>Client for $servicename$</summary>\n" , "servicename" , |
419 | GetServiceClassName(service)); |
420 | out->Print("public partial class $name$ : grpc::ClientBase<$name$>\n" , "name" , |
421 | GetClientClassName(service)); |
422 | out->Print("{\n" ); |
423 | out->Indent(); |
424 | |
425 | // constructors |
426 | out->Print( |
427 | "/// <summary>Creates a new client for $servicename$</summary>\n" |
428 | "/// <param name=\"channel\">The channel to use to make remote " |
429 | "calls.</param>\n" , |
430 | "servicename" , GetServiceClassName(service)); |
431 | out->Print("public $name$(grpc::ChannelBase channel) : base(channel)\n" , |
432 | "name" , GetClientClassName(service)); |
433 | out->Print("{\n" ); |
434 | out->Print("}\n" ); |
435 | out->Print( |
436 | "/// <summary>Creates a new client for $servicename$ that uses a custom " |
437 | "<c>CallInvoker</c>.</summary>\n" |
438 | "/// <param name=\"callInvoker\">The callInvoker to use to make remote " |
439 | "calls.</param>\n" , |
440 | "servicename" , GetServiceClassName(service)); |
441 | out->Print( |
442 | "public $name$(grpc::CallInvoker callInvoker) : base(callInvoker)\n" , |
443 | "name" , GetClientClassName(service)); |
444 | out->Print("{\n" ); |
445 | out->Print("}\n" ); |
446 | out->Print( |
447 | "/// <summary>Protected parameterless constructor to allow creation" |
448 | " of test doubles.</summary>\n" ); |
449 | out->Print("protected $name$() : base()\n" , "name" , |
450 | GetClientClassName(service)); |
451 | out->Print("{\n" ); |
452 | out->Print("}\n" ); |
453 | out->Print( |
454 | "/// <summary>Protected constructor to allow creation of configured " |
455 | "clients.</summary>\n" |
456 | "/// <param name=\"configuration\">The client configuration.</param>\n" ); |
457 | out->Print( |
458 | "protected $name$(ClientBaseConfiguration configuration)" |
459 | " : base(configuration)\n" , |
460 | "name" , GetClientClassName(service)); |
461 | out->Print("{\n" ); |
462 | out->Print("}\n\n" ); |
463 | |
464 | for (int i = 0; i < service->method_count(); i++) { |
465 | const MethodDescriptor* method = service->method(i); |
466 | MethodType method_type = GetMethodType(method); |
467 | |
468 | if (method_type == METHODTYPE_NO_STREAMING) { |
469 | // unary calls have an extra synchronous stub method |
470 | GenerateDocCommentClientMethod(out, method, true, false); |
471 | out->Print( |
472 | "public virtual $response$ $methodname$($request$ request, " |
473 | "grpc::Metadata " |
474 | "headers = null, global::System.DateTime? deadline = null, " |
475 | "global::System.Threading.CancellationToken " |
476 | "cancellationToken = " |
477 | "default(global::System.Threading.CancellationToken))\n" , |
478 | "methodname" , method->name(), "request" , |
479 | GetClassName(method->input_type()), "response" , |
480 | GetClassName(method->output_type())); |
481 | out->Print("{\n" ); |
482 | out->Indent(); |
483 | out->Print( |
484 | "return $methodname$(request, new grpc::CallOptions(headers, " |
485 | "deadline, " |
486 | "cancellationToken));\n" , |
487 | "methodname" , method->name()); |
488 | out->Outdent(); |
489 | out->Print("}\n" ); |
490 | |
491 | // overload taking CallOptions as a param |
492 | GenerateDocCommentClientMethod(out, method, true, true); |
493 | out->Print( |
494 | "public virtual $response$ $methodname$($request$ request, " |
495 | "grpc::CallOptions options)\n" , |
496 | "methodname" , method->name(), "request" , |
497 | GetClassName(method->input_type()), "response" , |
498 | GetClassName(method->output_type())); |
499 | out->Print("{\n" ); |
500 | out->Indent(); |
501 | out->Print( |
502 | "return CallInvoker.BlockingUnaryCall($methodfield$, null, options, " |
503 | "request);\n" , |
504 | "methodfield" , GetMethodFieldName(method)); |
505 | out->Outdent(); |
506 | out->Print("}\n" ); |
507 | } |
508 | |
509 | std::string method_name = method->name(); |
510 | if (method_type == METHODTYPE_NO_STREAMING) { |
511 | method_name += "Async" ; // prevent name clash with synchronous method. |
512 | } |
513 | GenerateDocCommentClientMethod(out, method, false, false); |
514 | out->Print( |
515 | "public virtual $returntype$ " |
516 | "$methodname$($request_maybe$grpc::Metadata " |
517 | "headers = null, global::System.DateTime? deadline = null, " |
518 | "global::System.Threading.CancellationToken " |
519 | "cancellationToken = " |
520 | "default(global::System.Threading.CancellationToken))\n" , |
521 | "methodname" , method_name, "request_maybe" , |
522 | GetMethodRequestParamMaybe(method), "returntype" , |
523 | GetMethodReturnTypeClient(method)); |
524 | out->Print("{\n" ); |
525 | out->Indent(); |
526 | |
527 | out->Print( |
528 | "return $methodname$($request_maybe$new grpc::CallOptions(headers, " |
529 | "deadline, " |
530 | "cancellationToken));\n" , |
531 | "methodname" , method_name, "request_maybe" , |
532 | GetMethodRequestParamMaybe(method, true)); |
533 | out->Outdent(); |
534 | out->Print("}\n" ); |
535 | |
536 | // overload taking CallOptions as a param |
537 | GenerateDocCommentClientMethod(out, method, false, true); |
538 | out->Print( |
539 | "public virtual $returntype$ " |
540 | "$methodname$($request_maybe$grpc::CallOptions " |
541 | "options)\n" , |
542 | "methodname" , method_name, "request_maybe" , |
543 | GetMethodRequestParamMaybe(method), "returntype" , |
544 | GetMethodReturnTypeClient(method)); |
545 | out->Print("{\n" ); |
546 | out->Indent(); |
547 | switch (GetMethodType(method)) { |
548 | case METHODTYPE_NO_STREAMING: |
549 | out->Print( |
550 | "return CallInvoker.AsyncUnaryCall($methodfield$, null, options, " |
551 | "request);\n" , |
552 | "methodfield" , GetMethodFieldName(method)); |
553 | break; |
554 | case METHODTYPE_CLIENT_STREAMING: |
555 | out->Print( |
556 | "return CallInvoker.AsyncClientStreamingCall($methodfield$, null, " |
557 | "options);\n" , |
558 | "methodfield" , GetMethodFieldName(method)); |
559 | break; |
560 | case METHODTYPE_SERVER_STREAMING: |
561 | out->Print( |
562 | "return CallInvoker.AsyncServerStreamingCall($methodfield$, null, " |
563 | "options, request);\n" , |
564 | "methodfield" , GetMethodFieldName(method)); |
565 | break; |
566 | case METHODTYPE_BIDI_STREAMING: |
567 | out->Print( |
568 | "return CallInvoker.AsyncDuplexStreamingCall($methodfield$, null, " |
569 | "options);\n" , |
570 | "methodfield" , GetMethodFieldName(method)); |
571 | break; |
572 | default: |
573 | GOOGLE_LOG(FATAL) << "Can't get here." ; |
574 | } |
575 | out->Outdent(); |
576 | out->Print("}\n" ); |
577 | } |
578 | |
579 | // override NewInstance method |
580 | out->Print( |
581 | "/// <summary>Creates a new instance of client from given " |
582 | "<c>ClientBaseConfiguration</c>.</summary>\n" ); |
583 | out->Print( |
584 | "protected override $name$ NewInstance(ClientBaseConfiguration " |
585 | "configuration)\n" , |
586 | "name" , GetClientClassName(service)); |
587 | out->Print("{\n" ); |
588 | out->Indent(); |
589 | out->Print("return new $name$(configuration);\n" , "name" , |
590 | GetClientClassName(service)); |
591 | out->Outdent(); |
592 | out->Print("}\n" ); |
593 | |
594 | out->Outdent(); |
595 | out->Print("}\n" ); |
596 | out->Print("\n" ); |
597 | } |
598 | |
599 | void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor* service) { |
600 | out->Print( |
601 | "/// <summary>Creates service definition that can be registered with a " |
602 | "server</summary>\n" ); |
603 | out->Print( |
604 | "/// <param name=\"serviceImpl\">An object implementing the server-side" |
605 | " handling logic.</param>\n" ); |
606 | out->Print( |
607 | "public static grpc::ServerServiceDefinition BindService($implclass$ " |
608 | "serviceImpl)\n" , |
609 | "implclass" , GetServerClassName(service)); |
610 | out->Print("{\n" ); |
611 | out->Indent(); |
612 | |
613 | out->Print("return grpc::ServerServiceDefinition.CreateBuilder()" ); |
614 | out->Indent(); |
615 | out->Indent(); |
616 | for (int i = 0; i < service->method_count(); i++) { |
617 | const MethodDescriptor* method = service->method(i); |
618 | out->Print("\n.AddMethod($methodfield$, serviceImpl.$methodname$)" , |
619 | "methodfield" , GetMethodFieldName(method), "methodname" , |
620 | method->name()); |
621 | } |
622 | out->Print(".Build();\n" ); |
623 | out->Outdent(); |
624 | out->Outdent(); |
625 | |
626 | out->Outdent(); |
627 | out->Print("}\n" ); |
628 | out->Print("\n" ); |
629 | } |
630 | |
631 | void GenerateBindServiceWithBinderMethod(Printer* out, |
632 | const ServiceDescriptor* service) { |
633 | out->Print( |
634 | "/// <summary>Register service method with a service " |
635 | "binder with or without implementation. Useful when customizing the " |
636 | "service binding logic.\n" |
637 | "/// Note: this method is part of an experimental API that can change or " |
638 | "be " |
639 | "removed without any prior notice.</summary>\n" ); |
640 | out->Print( |
641 | "/// <param name=\"serviceBinder\">Service methods will be bound by " |
642 | "calling <c>AddMethod</c> on this object." |
643 | "</param>\n" ); |
644 | out->Print( |
645 | "/// <param name=\"serviceImpl\">An object implementing the server-side" |
646 | " handling logic.</param>\n" ); |
647 | out->Print( |
648 | "public static void BindService(grpc::ServiceBinderBase serviceBinder, " |
649 | "$implclass$ " |
650 | "serviceImpl)\n" , |
651 | "implclass" , GetServerClassName(service)); |
652 | out->Print("{\n" ); |
653 | out->Indent(); |
654 | |
655 | for (int i = 0; i < service->method_count(); i++) { |
656 | const MethodDescriptor* method = service->method(i); |
657 | out->Print( |
658 | "serviceBinder.AddMethod($methodfield$, serviceImpl == null ? null : " |
659 | "new $servermethodtype$<$inputtype$, $outputtype$>(" |
660 | "serviceImpl.$methodname$));\n" , |
661 | "methodfield" , GetMethodFieldName(method), "servermethodtype" , |
662 | GetCSharpServerMethodType(GetMethodType(method)), "inputtype" , |
663 | GetClassName(method->input_type()), "outputtype" , |
664 | GetClassName(method->output_type()), "methodname" , method->name()); |
665 | } |
666 | |
667 | out->Outdent(); |
668 | out->Print("}\n" ); |
669 | out->Print("\n" ); |
670 | } |
671 | |
672 | void GenerateService(Printer* out, const ServiceDescriptor* service, |
673 | bool generate_client, bool generate_server, |
674 | bool internal_access) { |
675 | GenerateDocCommentBody(out, service); |
676 | out->Print("$access_level$ static partial class $classname$\n" , |
677 | "access_level" , GetAccessLevel(internal_access), "classname" , |
678 | GetServiceClassName(service)); |
679 | out->Print("{\n" ); |
680 | out->Indent(); |
681 | out->Print("static readonly string $servicenamefield$ = \"$servicename$\";\n" , |
682 | "servicenamefield" , GetServiceNameFieldName(), "servicename" , |
683 | service->full_name()); |
684 | out->Print("\n" ); |
685 | |
686 | GenerateMarshallerFields(out, service); |
687 | for (int i = 0; i < service->method_count(); i++) { |
688 | GenerateStaticMethodField(out, service->method(i)); |
689 | } |
690 | GenerateServiceDescriptorProperty(out, service); |
691 | |
692 | if (generate_server) { |
693 | GenerateServerClass(out, service); |
694 | } |
695 | if (generate_client) { |
696 | GenerateClientStub(out, service); |
697 | } |
698 | if (generate_server) { |
699 | GenerateBindServiceMethod(out, service); |
700 | GenerateBindServiceWithBinderMethod(out, service); |
701 | } |
702 | |
703 | out->Outdent(); |
704 | out->Print("}\n" ); |
705 | } |
706 | |
707 | } // anonymous namespace |
708 | |
709 | grpc::string GetServices(const FileDescriptor* file, bool generate_client, |
710 | bool generate_server, bool internal_access) { |
711 | grpc::string output; |
712 | { |
713 | // Scope the output stream so it closes and finalizes output to the string. |
714 | |
715 | StringOutputStream output_stream(&output); |
716 | Printer out(&output_stream, '$'); |
717 | |
718 | // Don't write out any output if there no services, to avoid empty service |
719 | // files being generated for proto files that don't declare any. |
720 | if (file->service_count() == 0) { |
721 | return output; |
722 | } |
723 | |
724 | // Write out a file header. |
725 | out.Print("// <auto-generated>\n" ); |
726 | out.Print( |
727 | "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" ); |
728 | out.Print("// source: $filename$\n" , "filename" , file->name()); |
729 | out.Print("// </auto-generated>\n" ); |
730 | |
731 | // use C++ style as there are no file-level XML comments in .NET |
732 | grpc::string = GetCsharpComments(file, true); |
733 | if (!leading_comments.empty()) { |
734 | out.Print("// Original file comments:\n" ); |
735 | out.PrintRaw(leading_comments.c_str()); |
736 | } |
737 | |
738 | out.Print("#pragma warning disable 0414, 1591\n" ); |
739 | |
740 | out.Print("#region Designer generated code\n" ); |
741 | out.Print("\n" ); |
742 | out.Print("using grpc = global::Grpc.Core;\n" ); |
743 | out.Print("\n" ); |
744 | |
745 | grpc::string file_namespace = GetFileNamespace(file); |
746 | if (file_namespace != "" ) { |
747 | out.Print("namespace $namespace$ {\n" , "namespace" , file_namespace); |
748 | out.Indent(); |
749 | } |
750 | for (int i = 0; i < file->service_count(); i++) { |
751 | GenerateService(&out, file->service(i), generate_client, generate_server, |
752 | internal_access); |
753 | } |
754 | if (file_namespace != "" ) { |
755 | out.Outdent(); |
756 | out.Print("}\n" ); |
757 | } |
758 | out.Print("#endregion\n" ); |
759 | } |
760 | return output; |
761 | } |
762 | |
763 | } // namespace grpc_csharp_generator |
764 | |