blob: 29c359c539572fdbb74e640140d021977fb68439 [file] [log] [blame]
Jan Tattermusch2d924952015-05-06 10:23:17 -07001/*
2 *
Craig Tiller6169d5f2016-03-31 07:46:18 -07003 * Copyright 2015, Google Inc.
Jan Tattermusch2d924952015-05-06 10:23:17 -07004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include <cctype>
35#include <map>
yang-gc3ee1d52015-08-28 11:33:52 -070036#include <sstream>
Jan Tattermusch2d924952015-05-06 10:23:17 -070037#include <vector>
38
Jan Tattermusch741e64c2015-08-03 08:08:56 -070039#include "src/compiler/csharp_generator.h"
Jan Tattermusch2d924952015-05-06 10:23:17 -070040#include "src/compiler/config.h"
Jan Tattermuschb5897bf2015-05-07 15:45:37 -070041#include "src/compiler/csharp_generator_helpers.h"
Jan Tattermusch2d924952015-05-06 10:23:17 -070042#include "src/compiler/csharp_generator.h"
43
Jan Tattermusch741e64c2015-08-03 08:08:56 -070044
45using google::protobuf::compiler::csharp::GetFileNamespace;
46using google::protobuf::compiler::csharp::GetClassName;
Jan Tattermuschda717f42016-01-20 13:12:35 -080047using google::protobuf::compiler::csharp::GetReflectionClassName;
Jan Tattermusch2d924952015-05-06 10:23:17 -070048using grpc::protobuf::FileDescriptor;
49using grpc::protobuf::Descriptor;
50using grpc::protobuf::ServiceDescriptor;
51using grpc::protobuf::MethodDescriptor;
52using grpc::protobuf::io::Printer;
53using grpc::protobuf::io::StringOutputStream;
Jan Tattermuschb5897bf2015-05-07 15:45:37 -070054using grpc_generator::MethodType;
Jan Tattermusch59c20ed2016-04-28 09:12:13 -070055using grpc_generator::GetCppComments;
Jan Tattermuschb5897bf2015-05-07 15:45:37 -070056using grpc_generator::GetMethodType;
57using grpc_generator::METHODTYPE_NO_STREAMING;
58using grpc_generator::METHODTYPE_CLIENT_STREAMING;
59using grpc_generator::METHODTYPE_SERVER_STREAMING;
60using grpc_generator::METHODTYPE_BIDI_STREAMING;
Jan Tattermusch41f9f332015-05-20 08:52:00 -070061using grpc_generator::StringReplace;
Jan Tattermusch2d924952015-05-06 10:23:17 -070062using std::map;
63using std::vector;
64
Jan Tattermusch741e64c2015-08-03 08:08:56 -070065
Jan Tattermusch2d924952015-05-06 10:23:17 -070066namespace grpc_csharp_generator {
67namespace {
68
Jan Tattermusch59c20ed2016-04-28 09:12:13 -070069// This function is a massaged version of
70// https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc
71// Currently, we cannot easily reuse the functionality as
72// google/protobuf/compiler/csharp/csharp_doc_comment.h is not a public header.
73// TODO(jtattermusch): reuse the functionality from google/protobuf.
74void GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer, grpc::protobuf::SourceLocation location) {
75 grpc::string comments = location.leading_comments.empty() ?
76 location.trailing_comments : location.leading_comments;
77 if (comments.empty()) {
78 return;
79 }
80 // XML escaping... no need for apostrophes etc as the whole text is going to be a child
81 // node of a summary element, not part of an attribute.
82 comments = grpc_generator::StringReplace(comments, "&", "&amp;", true);
83 comments = grpc_generator::StringReplace(comments, "<", "&lt;", true);
84
85 std::vector<grpc::string> lines;
86 grpc_generator::Split(comments, '\n', &lines);
87 // TODO: We really should work out which part to put in the summary and which to put in the remarks...
88 // but that needs to be part of a bigger effort to understand the markdown better anyway.
89 printer->Print("/// <summary>\n");
90 bool last_was_empty = false;
91 // We squash multiple blank lines down to one, and remove any trailing blank lines. We need
92 // to preserve the blank lines themselves, as this is relevant in the markdown.
93 // Note that we can't remove leading or trailing whitespace as *that's* relevant in markdown too.
94 // (We don't skip "just whitespace" lines, either.)
95 for (std::vector<grpc::string>::iterator it = lines.begin(); it != lines.end(); ++it) {
96 grpc::string line = *it;
97 if (line.empty()) {
98 last_was_empty = true;
99 } else {
100 if (last_was_empty) {
101 printer->Print("///\n");
102 }
103 last_was_empty = false;
104 printer->Print("/// $line$\n", "line", *it);
105 }
106 }
107 printer->Print("/// </summary>\n");
108}
109
110template <typename DescriptorType>
111void GenerateDocCommentBody(
112 grpc::protobuf::io::Printer* printer, const DescriptorType* descriptor) {
113 grpc::protobuf::SourceLocation location;
114 if (descriptor->GetSourceLocation(&location)) {
115 GenerateDocCommentBodyImpl(printer, location);
116 }
117}
118
Jan Tattermusch2d924952015-05-06 10:23:17 -0700119std::string GetServiceClassName(const ServiceDescriptor* service) {
120 return service->name();
121}
122
123std::string GetClientInterfaceName(const ServiceDescriptor* service) {
124 return "I" + service->name() + "Client";
125}
126
127std::string GetClientClassName(const ServiceDescriptor* service) {
128 return service->name() + "Client";
129}
130
131std::string GetServerInterfaceName(const ServiceDescriptor* service) {
132 return "I" + service->name();
133}
134
Jan Tattermusch10a002d2016-03-14 15:22:19 -0700135std::string GetServerClassName(const ServiceDescriptor* service) {
136 return service->name() + "Base";
137}
138
Jan Tattermusch2d924952015-05-06 10:23:17 -0700139std::string GetCSharpMethodType(MethodType method_type) {
140 switch (method_type) {
141 case METHODTYPE_NO_STREAMING:
142 return "MethodType.Unary";
143 case METHODTYPE_CLIENT_STREAMING:
144 return "MethodType.ClientStreaming";
145 case METHODTYPE_SERVER_STREAMING:
146 return "MethodType.ServerStreaming";
147 case METHODTYPE_BIDI_STREAMING:
148 return "MethodType.DuplexStreaming";
149 }
150 GOOGLE_LOG(FATAL)<< "Can't get here.";
151 return "";
152}
153
154std::string GetServiceNameFieldName() {
155 return "__ServiceName";
156}
157
158std::string GetMarshallerFieldName(const Descriptor *message) {
159 return "__Marshaller_" + message->name();
160}
161
162std::string GetMethodFieldName(const MethodDescriptor *method) {
163 return "__Method_" + method->name();
164}
165
Jan Tattermusch8cd4ae92016-03-21 18:46:32 -0700166std::string GetMethodRequestParamMaybe(const MethodDescriptor *method,
167 bool invocation_param=false) {
Jan Tattermusch2d924952015-05-06 10:23:17 -0700168 if (method->client_streaming()) {
169 return "";
170 }
Jan Tattermusch8cd4ae92016-03-21 18:46:32 -0700171 if (invocation_param) {
172 return "request, ";
173 }
Jan Tattermusch41f9f332015-05-20 08:52:00 -0700174 return GetClassName(method->input_type()) + " request, ";
Jan Tattermusch2d924952015-05-06 10:23:17 -0700175}
176
Jan Tattermusch4e0f73c2016-04-25 16:11:03 -0700177std::string GetAccessLevel(bool internal_access) {
178 return internal_access ? "internal" : "public";
179}
180
Jan Tattermusch2d924952015-05-06 10:23:17 -0700181std::string GetMethodReturnTypeClient(const MethodDescriptor *method) {
182 switch (GetMethodType(method)) {
183 case METHODTYPE_NO_STREAMING:
Jan Tattermusch5269d162015-07-21 11:56:42 -0700184 return "AsyncUnaryCall<" + GetClassName(method->output_type()) + ">";
Jan Tattermusch2d924952015-05-06 10:23:17 -0700185 case METHODTYPE_CLIENT_STREAMING:
Jan Tattermusch41f9f332015-05-20 08:52:00 -0700186 return "AsyncClientStreamingCall<" + GetClassName(method->input_type())
187 + ", " + GetClassName(method->output_type()) + ">";
Jan Tattermusch2d924952015-05-06 10:23:17 -0700188 case METHODTYPE_SERVER_STREAMING:
Jan Tattermusch41f9f332015-05-20 08:52:00 -0700189 return "AsyncServerStreamingCall<" + GetClassName(method->output_type())
Jan Tattermusch2d924952015-05-06 10:23:17 -0700190 + ">";
191 case METHODTYPE_BIDI_STREAMING:
Jan Tattermusch41f9f332015-05-20 08:52:00 -0700192 return "AsyncDuplexStreamingCall<" + GetClassName(method->input_type())
193 + ", " + GetClassName(method->output_type()) + ">";
Jan Tattermusch2d924952015-05-06 10:23:17 -0700194 }
195 GOOGLE_LOG(FATAL)<< "Can't get here.";
196 return "";
197}
198
199std::string GetMethodRequestParamServer(const MethodDescriptor *method) {
200 switch (GetMethodType(method)) {
201 case METHODTYPE_NO_STREAMING:
202 case METHODTYPE_SERVER_STREAMING:
Jan Tattermusch41f9f332015-05-20 08:52:00 -0700203 return GetClassName(method->input_type()) + " request";
Jan Tattermusch2d924952015-05-06 10:23:17 -0700204 case METHODTYPE_CLIENT_STREAMING:
205 case METHODTYPE_BIDI_STREAMING:
Jan Tattermusch41f9f332015-05-20 08:52:00 -0700206 return "IAsyncStreamReader<" + GetClassName(method->input_type())
Jan Tattermusch2d924952015-05-06 10:23:17 -0700207 + "> requestStream";
208 }
209 GOOGLE_LOG(FATAL)<< "Can't get here.";
210 return "";
211}
212
213std::string GetMethodReturnTypeServer(const MethodDescriptor *method) {
214 switch (GetMethodType(method)) {
215 case METHODTYPE_NO_STREAMING:
216 case METHODTYPE_CLIENT_STREAMING:
Kirill Katsnelson0ddf46f2016-04-19 18:49:31 -0700217 return "global::System.Threading.Tasks.Task<" + GetClassName(method->output_type()) + ">";
Jan Tattermusch2d924952015-05-06 10:23:17 -0700218 case METHODTYPE_SERVER_STREAMING:
219 case METHODTYPE_BIDI_STREAMING:
Kirill Katsnelson0ddf46f2016-04-19 18:49:31 -0700220 return "global::System.Threading.Tasks.Task";
Jan Tattermusch2d924952015-05-06 10:23:17 -0700221 }
222 GOOGLE_LOG(FATAL)<< "Can't get here.";
223 return "";
224}
225
226std::string GetMethodResponseStreamMaybe(const MethodDescriptor *method) {
227 switch (GetMethodType(method)) {
228 case METHODTYPE_NO_STREAMING:
229 case METHODTYPE_CLIENT_STREAMING:
230 return "";
231 case METHODTYPE_SERVER_STREAMING:
232 case METHODTYPE_BIDI_STREAMING:
Jan Tattermusch41f9f332015-05-20 08:52:00 -0700233 return ", IServerStreamWriter<" + GetClassName(method->output_type())
Jan Tattermusch2d924952015-05-06 10:23:17 -0700234 + "> responseStream";
235 }
236 GOOGLE_LOG(FATAL)<< "Can't get here.";
237 return "";
238}
239
240// Gets vector of all messages used as input or output types.
241std::vector<const Descriptor*> GetUsedMessages(
242 const ServiceDescriptor *service) {
243 std::set<const Descriptor*> descriptor_set;
244 std::vector<const Descriptor*> result; // vector is to maintain stable ordering
245 for (int i = 0; i < service->method_count(); i++) {
246 const MethodDescriptor *method = service->method(i);
247 if (descriptor_set.find(method->input_type()) == descriptor_set.end()) {
248 descriptor_set.insert(method->input_type());
249 result.push_back(method->input_type());
250 }
251 if (descriptor_set.find(method->output_type()) == descriptor_set.end()) {
252 descriptor_set.insert(method->output_type());
253 result.push_back(method->output_type());
254 }
255 }
256 return result;
257}
258
259void GenerateMarshallerFields(Printer* out, const ServiceDescriptor *service) {
260 std::vector<const Descriptor*> used_messages = GetUsedMessages(service);
261 for (size_t i = 0; i < used_messages.size(); i++) {
262 const Descriptor *message = used_messages[i];
263 out->Print(
Jan Tattermuschad75dd12015-08-03 09:43:07 -0700264 "static readonly Marshaller<$type$> $fieldname$ = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), $type$.Parser.ParseFrom);\n",
Jan Tattermusch2d924952015-05-06 10:23:17 -0700265 "fieldname", GetMarshallerFieldName(message), "type",
Jan Tattermusch41f9f332015-05-20 08:52:00 -0700266 GetClassName(message));
Jan Tattermusch2d924952015-05-06 10:23:17 -0700267 }
268 out->Print("\n");
269}
270
271void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) {
272 out->Print(
273 "static readonly Method<$request$, $response$> $fieldname$ = new Method<$request$, $response$>(\n",
274 "fieldname", GetMethodFieldName(method), "request",
Jan Tattermusch41f9f332015-05-20 08:52:00 -0700275 GetClassName(method->input_type()), "response",
276 GetClassName(method->output_type()));
Jan Tattermusch2d924952015-05-06 10:23:17 -0700277 out->Indent();
278 out->Indent();
279 out->Print("$methodtype$,\n", "methodtype",
280 GetCSharpMethodType(GetMethodType(method)));
Jan Tattermuscha9ddd022015-08-05 03:04:58 -0700281 out->Print("$servicenamefield$,\n", "servicenamefield",
282 GetServiceNameFieldName());
Jan Tattermusch2d924952015-05-06 10:23:17 -0700283 out->Print("\"$methodname$\",\n", "methodname", method->name());
284 out->Print("$requestmarshaller$,\n", "requestmarshaller",
285 GetMarshallerFieldName(method->input_type()));
286 out->Print("$responsemarshaller$);\n", "responsemarshaller",
287 GetMarshallerFieldName(method->output_type()));
288 out->Print("\n");
289 out->Outdent();
290 out->Outdent();
291}
292
Jan Tattermusche6af5d12015-08-03 10:57:43 -0700293void GenerateServiceDescriptorProperty(Printer* out, const ServiceDescriptor *service) {
yang-gc3ee1d52015-08-28 11:33:52 -0700294 std::ostringstream index;
295 index << service->index();
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700296 out->Print("/// <summary>Service descriptor</summary>\n");
Jan Tattermusche6af5d12015-08-03 10:57:43 -0700297 out->Print("public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor\n");
298 out->Print("{\n");
299 out->Print(" get { return $umbrella$.Descriptor.Services[$index$]; }\n",
Jan Tattermuschda717f42016-01-20 13:12:35 -0800300 "umbrella", GetReflectionClassName(service->file()), "index",
yang-gc3ee1d52015-08-28 11:33:52 -0700301 index.str());
Jan Tattermusche6af5d12015-08-03 10:57:43 -0700302 out->Print("}\n");
303 out->Print("\n");
304}
305
Jan Tattermusch2d924952015-05-06 10:23:17 -0700306void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700307 out->Print("/// <summary>Client for $servicename$</summary>\n",
308 "servicename", GetServiceClassName(service));
Jan Tattermusch5a4e1e32016-03-25 16:51:55 -0700309 out->Print("[System.Obsolete(\"Client side interfaced will be removed "
310 "in the next release. Use client class directly.\")]\n");
Jan Tattermusch2d924952015-05-06 10:23:17 -0700311 out->Print("public interface $name$\n", "name",
312 GetClientInterfaceName(service));
313 out->Print("{\n");
314 out->Indent();
315 for (int i = 0; i < service->method_count(); i++) {
316 const MethodDescriptor *method = service->method(i);
317 MethodType method_type = GetMethodType(method);
318
319 if (method_type == METHODTYPE_NO_STREAMING) {
320 // unary calls have an extra synchronous stub method
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700321 GenerateDocCommentBody(out, method);
Jan Tattermusch2d924952015-05-06 10:23:17 -0700322 out->Print(
Jan Tattermusch74529562015-07-23 14:04:51 -0700323 "$response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));\n",
Jan Tattermusch2d924952015-05-06 10:23:17 -0700324 "methodname", method->name(), "request",
Jan Tattermusch41f9f332015-05-20 08:52:00 -0700325 GetClassName(method->input_type()), "response",
326 GetClassName(method->output_type()));
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700327
Jan Tattermusche5e57ad2015-08-05 14:54:08 -0700328 // overload taking CallOptions as a param
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700329 GenerateDocCommentBody(out, method);
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700330 out->Print(
Jan Tattermusche5e57ad2015-08-05 14:54:08 -0700331 "$response$ $methodname$($request$ request, CallOptions options);\n",
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700332 "methodname", method->name(), "request",
333 GetClassName(method->input_type()), "response",
334 GetClassName(method->output_type()));
Jan Tattermusch2d924952015-05-06 10:23:17 -0700335 }
336
337 std::string method_name = method->name();
338 if (method_type == METHODTYPE_NO_STREAMING) {
339 method_name += "Async"; // prevent name clash with synchronous method.
340 }
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700341 GenerateDocCommentBody(out, method);
Jan Tattermusch2d924952015-05-06 10:23:17 -0700342 out->Print(
Jan Tattermusch74529562015-07-23 14:04:51 -0700343 "$returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));\n",
Jan Tattermusch2d924952015-05-06 10:23:17 -0700344 "methodname", method_name, "request_maybe",
345 GetMethodRequestParamMaybe(method), "returntype",
346 GetMethodReturnTypeClient(method));
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700347
Jan Tattermusche5e57ad2015-08-05 14:54:08 -0700348 // overload taking CallOptions as a param
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700349 GenerateDocCommentBody(out, method);
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700350 out->Print(
Jan Tattermusche5e57ad2015-08-05 14:54:08 -0700351 "$returntype$ $methodname$($request_maybe$CallOptions options);\n",
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700352 "methodname", method_name, "request_maybe",
353 GetMethodRequestParamMaybe(method), "returntype",
354 GetMethodReturnTypeClient(method));
Jan Tattermusch2d924952015-05-06 10:23:17 -0700355 }
356 out->Outdent();
357 out->Print("}\n");
358 out->Print("\n");
359}
360
361void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) {
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700362 out->Print("/// <summary>Interface of server-side implementations of $servicename$</summary>\n",
363 "servicename", GetServiceClassName(service));
Jan Tattermusch10a002d2016-03-14 15:22:19 -0700364 out->Print("[System.Obsolete(\"Service implementations should inherit"
365 " from the generated abstract base class instead.\")]\n");
Jan Tattermusch2d924952015-05-06 10:23:17 -0700366 out->Print("public interface $name$\n", "name",
367 GetServerInterfaceName(service));
368 out->Print("{\n");
369 out->Indent();
370 for (int i = 0; i < service->method_count(); i++) {
371 const MethodDescriptor *method = service->method(i);
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700372 GenerateDocCommentBody(out, method);
Craig Tillerb256faa2015-07-23 11:28:16 -0700373 out->Print(
374 "$returntype$ $methodname$($request$$response_stream_maybe$, "
375 "ServerCallContext context);\n",
376 "methodname", method->name(), "returntype",
377 GetMethodReturnTypeServer(method), "request",
378 GetMethodRequestParamServer(method), "response_stream_maybe",
379 GetMethodResponseStreamMaybe(method));
Jan Tattermusch2d924952015-05-06 10:23:17 -0700380 }
381 out->Outdent();
382 out->Print("}\n");
383 out->Print("\n");
384}
385
Jan Tattermusch10a002d2016-03-14 15:22:19 -0700386void GenerateServerClass(Printer* out, const ServiceDescriptor *service) {
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700387 out->Print("/// <summary>Base class for server-side implementations of $servicename$</summary>\n",
388 "servicename", GetServiceClassName(service));
Jan Tattermusch10a002d2016-03-14 15:22:19 -0700389 out->Print("public abstract 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);
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700395 GenerateDocCommentBody(out, method);
Jan Tattermusch10a002d2016-03-14 15:22:19 -0700396 out->Print(
397 "public virtual $returntype$ $methodname$($request$$response_stream_maybe$, "
398 "ServerCallContext context)\n",
399 "methodname", method->name(), "returntype",
400 GetMethodReturnTypeServer(method), "request",
401 GetMethodRequestParamServer(method), "response_stream_maybe",
402 GetMethodResponseStreamMaybe(method));
403 out->Print("{\n");
404 out->Indent();
405 out->Print("throw new RpcException("
406 "new Status(StatusCode.Unimplemented, \"\"));\n");
407 out->Outdent();
408 out->Print("}\n\n");
409 }
410 out->Outdent();
411 out->Print("}\n");
412 out->Print("\n");
413}
414
Jan Tattermusch2d924952015-05-06 10:23:17 -0700415void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700416 out->Print("/// <summary>Client for $servicename$</summary>\n",
417 "servicename", GetServiceClassName(service));
Jan Tattermuschcc8a4b32016-04-25 13:40:00 -0700418 out->Print("#pragma warning disable 0618\n");
Jan Tattermusch2d924952015-05-06 10:23:17 -0700419 out->Print(
Jan Tattermusch5a4e1e32016-03-25 16:51:55 -0700420 "public class $name$ : ClientBase<$name$>, $interface$\n",
421 "name", GetClientClassName(service),
422 "interface", GetClientInterfaceName(service));
Jan Tattermuschcc8a4b32016-04-25 13:40:00 -0700423 out->Print("#pragma warning restore 0618\n");
Jan Tattermusch2d924952015-05-06 10:23:17 -0700424 out->Print("{\n");
425 out->Indent();
426
427 // constructors
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700428 out->Print("public $name$(Channel channel) : base(channel)\n",
429 "name", GetClientClassName(service));
Jan Tattermusch2d924952015-05-06 10:23:17 -0700430 out->Print("{\n");
431 out->Print("}\n");
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700432 out->Print("public $name$(CallInvoker callInvoker) : base(callInvoker)\n",
433 "name", GetClientClassName(service));
434 out->Print("{\n");
Jan Tattermuschefb77842016-03-23 07:47:36 -0700435 out->Print("}\n");
Jan Tattermusch2f0a8372016-03-23 09:16:49 -0700436 out->Print("///<summary>Protected parameterless constructor to allow creation"
Jan Tattermuschefb77842016-03-23 07:47:36 -0700437 " of test doubles.</summary>\n");
438 out->Print("protected $name$() : base()\n",
439 "name", GetClientClassName(service));
440 out->Print("{\n");
Jan Tattermusch2f0a8372016-03-23 09:16:49 -0700441 out->Print("}\n");
442 out->Print("///<summary>Protected constructor to allow creation of configured"
443 " clients.</summary>\n");
444 out->Print("protected $name$(ClientBaseConfiguration configuration)"
445 " : base(configuration)\n",
446 "name", GetClientClassName(service));
447 out->Print("{\n");
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700448 out->Print("}\n\n");
Jan Tattermusch2d924952015-05-06 10:23:17 -0700449
450 for (int i = 0; i < service->method_count(); i++) {
451 const MethodDescriptor *method = service->method(i);
452 MethodType method_type = GetMethodType(method);
453
454 if (method_type == METHODTYPE_NO_STREAMING) {
455 // unary calls have an extra synchronous stub method
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700456 GenerateDocCommentBody(out, method);
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700457 out->Print("public virtual $response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n",
458 "methodname", method->name(), "request",
459 GetClassName(method->input_type()), "response",
460 GetClassName(method->output_type()));
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700461 out->Print("{\n");
462 out->Indent();
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700463 out->Print("return $methodname$(request, new CallOptions(headers, deadline, cancellationToken));\n",
464 "methodname", method->name());
465 out->Outdent();
466 out->Print("}\n");
467
468 // overload taking CallOptions as a param
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700469 GenerateDocCommentBody(out, method);
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700470 out->Print("public virtual $response$ $methodname$($request$ request, CallOptions options)\n",
471 "methodname", method->name(), "request",
472 GetClassName(method->input_type()), "response",
473 GetClassName(method->output_type()));
474 out->Print("{\n");
475 out->Indent();
Jan Tattermuschb455bcc2016-03-22 16:08:18 -0700476 out->Print("return CallInvoker.BlockingUnaryCall($methodfield$, null, options, request);\n",
Jan Tattermusch641cb1b2015-08-05 03:51:46 -0700477 "methodfield", GetMethodFieldName(method));
Jan Tattermusch2d924952015-05-06 10:23:17 -0700478 out->Outdent();
479 out->Print("}\n");
480 }
481
482 std::string method_name = method->name();
483 if (method_type == METHODTYPE_NO_STREAMING) {
484 method_name += "Async"; // prevent name clash with synchronous method.
485 }
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700486 GenerateDocCommentBody(out, method);
Jan Tattermusch2d924952015-05-06 10:23:17 -0700487 out->Print(
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700488 "public virtual $returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n",
489 "methodname", method_name, "request_maybe",
490 GetMethodRequestParamMaybe(method), "returntype",
491 GetMethodReturnTypeClient(method));
492 out->Print("{\n");
493 out->Indent();
494
495 out->Print("return $methodname$($request_maybe$new CallOptions(headers, deadline, cancellationToken));\n",
496 "methodname", method_name,
497 "request_maybe", GetMethodRequestParamMaybe(method, true));
498 out->Outdent();
499 out->Print("}\n");
500
501 // overload taking CallOptions as a param
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700502 GenerateDocCommentBody(out, method);
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700503 out->Print(
504 "public virtual $returntype$ $methodname$($request_maybe$CallOptions options)\n",
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700505 "methodname", method_name, "request_maybe",
506 GetMethodRequestParamMaybe(method), "returntype",
507 GetMethodReturnTypeClient(method));
508 out->Print("{\n");
509 out->Indent();
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700510 switch (GetMethodType(method)) {
511 case METHODTYPE_NO_STREAMING:
Jan Tattermuschb455bcc2016-03-22 16:08:18 -0700512 out->Print("return CallInvoker.AsyncUnaryCall($methodfield$, null, options, request);\n",
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700513 "methodfield", GetMethodFieldName(method));
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700514 break;
515 case METHODTYPE_CLIENT_STREAMING:
Jan Tattermuschb455bcc2016-03-22 16:08:18 -0700516 out->Print("return CallInvoker.AsyncClientStreamingCall($methodfield$, null, options);\n",
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700517 "methodfield", GetMethodFieldName(method));
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700518 break;
519 case METHODTYPE_SERVER_STREAMING:
520 out->Print(
Jan Tattermuschb455bcc2016-03-22 16:08:18 -0700521 "return CallInvoker.AsyncServerStreamingCall($methodfield$, null, options, request);\n",
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700522 "methodfield", GetMethodFieldName(method));
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700523 break;
524 case METHODTYPE_BIDI_STREAMING:
Jan Tattermuschb455bcc2016-03-22 16:08:18 -0700525 out->Print("return CallInvoker.AsyncDuplexStreamingCall($methodfield$, null, options);\n",
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700526 "methodfield", GetMethodFieldName(method));
Jan Tattermusch5e10f182015-08-05 00:13:02 -0700527 break;
528 default:
529 GOOGLE_LOG(FATAL)<< "Can't get here.";
530 }
Jan Tattermusch2d924952015-05-06 10:23:17 -0700531 out->Outdent();
532 out->Print("}\n");
533 }
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700534
535 // override NewInstance method
Jan Tattermusch2f0a8372016-03-23 09:16:49 -0700536 out->Print("protected override $name$ NewInstance(ClientBaseConfiguration configuration)\n",
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700537 "name", GetClientClassName(service));
538 out->Print("{\n");
539 out->Indent();
Jan Tattermusch2f0a8372016-03-23 09:16:49 -0700540 out->Print("return new $name$(configuration);\n",
Jan Tattermusch055c2dd2016-03-22 14:43:56 -0700541 "name", GetClientClassName(service));
542 out->Outdent();
543 out->Print("}\n");
544
Jan Tattermusch2d924952015-05-06 10:23:17 -0700545 out->Outdent();
546 out->Print("}\n");
547 out->Print("\n");
548}
549
Jan Tattermusch10a002d2016-03-14 15:22:19 -0700550void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service,
551 bool use_server_class) {
Jan Tattermusch2d924952015-05-06 10:23:17 -0700552 out->Print(
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700553 "/// <summary>Creates service definition that can be registered with a server</summary>\n");
Jan Tattermuschcc8a4b32016-04-25 13:40:00 -0700554 out->Print("#pragma warning disable 0618\n");
Jan Tattermusch2d924952015-05-06 10:23:17 -0700555 out->Print(
556 "public static ServerServiceDefinition BindService($interface$ serviceImpl)\n",
Jan Tattermusch10a002d2016-03-14 15:22:19 -0700557 "interface", use_server_class ? GetServerClassName(service) :
558 GetServerInterfaceName(service));
Jan Tattermuschcc8a4b32016-04-25 13:40:00 -0700559 out->Print("#pragma warning restore 0618\n");
Jan Tattermusch2d924952015-05-06 10:23:17 -0700560 out->Print("{\n");
561 out->Indent();
562
563 out->Print(
564 "return ServerServiceDefinition.CreateBuilder($servicenamefield$)\n",
565 "servicenamefield", GetServiceNameFieldName());
566 out->Indent();
567 out->Indent();
568 for (int i = 0; i < service->method_count(); i++) {
569 const MethodDescriptor *method = service->method(i);
570 out->Print(".AddMethod($methodfield$, serviceImpl.$methodname$)",
571 "methodfield", GetMethodFieldName(method), "methodname",
572 method->name());
573 if (i == service->method_count() - 1) {
574 out->Print(".Build();");
575 }
576 out->Print("\n");
577 }
578 out->Outdent();
579 out->Outdent();
580
581 out->Outdent();
582 out->Print("}\n");
583 out->Print("\n");
584}
585
586void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) {
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700587 out->Print("/// <summary>Creates a new client for $servicename$</summary>\n",
588 "servicename", GetServiceClassName(service));
Jan Tattermuschb5332812015-07-14 19:29:35 -0700589 out->Print("public static $classname$ NewClient(Channel channel)\n",
590 "classname", GetClientClassName(service));
Jan Tattermusch2d924952015-05-06 10:23:17 -0700591 out->Print("{\n");
592 out->Indent();
593 out->Print("return new $classname$(channel);\n", "classname",
594 GetClientClassName(service));
595 out->Outdent();
596 out->Print("}\n");
597 out->Print("\n");
Jan Tattermusch2d924952015-05-06 10:23:17 -0700598}
599
Jan Tattermusch44f7c542016-04-25 14:50:27 -0700600void GenerateService(Printer* out, const ServiceDescriptor *service,
Jan Tattermusch4e0f73c2016-04-25 16:11:03 -0700601 bool generate_client, bool generate_server,
602 bool internal_access) {
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700603 GenerateDocCommentBody(out, service);
Jan Tattermusch4e0f73c2016-04-25 16:11:03 -0700604 out->Print("$access_level$ static class $classname$\n", "access_level",
605 GetAccessLevel(internal_access), "classname",
Jan Tattermusch2d924952015-05-06 10:23:17 -0700606 GetServiceClassName(service));
607 out->Print("{\n");
608 out->Indent();
609 out->Print("static readonly string $servicenamefield$ = \"$servicename$\";\n",
610 "servicenamefield", GetServiceNameFieldName(), "servicename",
611 service->full_name());
612 out->Print("\n");
613
614 GenerateMarshallerFields(out, service);
615 for (int i = 0; i < service->method_count(); i++) {
616 GenerateStaticMethodField(out, service->method(i));
617 }
Jan Tattermusche6af5d12015-08-03 10:57:43 -0700618 GenerateServiceDescriptorProperty(out, service);
Jan Tattermusch44f7c542016-04-25 14:50:27 -0700619
620 if (generate_client) {
621 GenerateClientInterface(out, service);
622 }
623 if (generate_server) {
624 GenerateServerInterface(out, service);
625 GenerateServerClass(out, service);
626 }
627 if (generate_client) {
628 GenerateClientStub(out, service);
629 GenerateNewStubMethods(out, service);
630 }
631 if (generate_server) {
632 GenerateBindServiceMethod(out, service, false);
633 GenerateBindServiceMethod(out, service, true);
634 }
Jan Tattermusch2d924952015-05-06 10:23:17 -0700635
636 out->Outdent();
637 out->Print("}\n");
638}
639
640} // anonymous namespace
641
Jan Tattermusch5f8872f2016-04-25 15:57:10 -0700642grpc::string GetServices(const FileDescriptor *file, bool generate_client,
Jan Tattermusch4e0f73c2016-04-25 16:11:03 -0700643 bool generate_server, bool internal_access) {
Jan Tattermusch2d924952015-05-06 10:23:17 -0700644 grpc::string output;
Jan Tattermusch5dcebd92015-05-27 15:30:59 -0700645 {
646 // Scope the output stream so it closes and finalizes output to the string.
Jan Tattermusch2d924952015-05-06 10:23:17 -0700647
Jan Tattermusch5dcebd92015-05-27 15:30:59 -0700648 StringOutputStream output_stream(&output);
649 Printer out(&output_stream, '$');
650
651 // Don't write out any output if there no services, to avoid empty service
652 // files being generated for proto files that don't declare any.
653 if (file->service_count() == 0) {
654 return output;
655 }
656
657 // Write out a file header.
658 out.Print("// Generated by the protocol buffer compiler. DO NOT EDIT!\n");
659 out.Print("// source: $filename$\n", "filename", file->name());
Jan Tattermusch59c20ed2016-04-28 09:12:13 -0700660
661 // use C++ style as there are no file-level XML comments in .NET
662 grpc::string leading_comments = GetCppComments(file, true);
663 if (!leading_comments.empty()) {
664 out.Print("// Original file comments:\n");
665 out.Print(leading_comments.c_str());
666 }
667
Jan Tattermusch5dcebd92015-05-27 15:30:59 -0700668 out.Print("#region Designer generated code\n");
669 out.Print("\n");
670 out.Print("using System;\n");
671 out.Print("using System.Threading;\n");
672 out.Print("using System.Threading.Tasks;\n");
673 out.Print("using Grpc.Core;\n");
Jan Tattermusch5dcebd92015-05-27 15:30:59 -0700674 out.Print("\n");
675
676 out.Print("namespace $namespace$ {\n", "namespace", GetFileNamespace(file));
677 out.Indent();
678 for (int i = 0; i < file->service_count(); i++) {
Jan Tattermusch4e0f73c2016-04-25 16:11:03 -0700679 GenerateService(&out, file->service(i), generate_client, generate_server,
680 internal_access);
Jan Tattermusch5dcebd92015-05-27 15:30:59 -0700681 }
682 out.Outdent();
683 out.Print("}\n");
684 out.Print("#endregion\n");
Jan Tattermusch2d924952015-05-06 10:23:17 -0700685 }
Jan Tattermusch2d924952015-05-06 10:23:17 -0700686 return output;
687}
688
689} // namespace grpc_csharp_generator