csharp_generator.cc 25 KB

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