1 // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2
3 // Copyright (c) 2016, Stepan Koltsov
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 use std::collections::HashMap;
25 use std::io::Write;
26
27 use protobuf::compiler_plugin;
28 use protobuf::descriptor::*;
29 use protobuf::descriptorx::*;
30
31 struct CodeWriter<'a> {
32 writer: &'a mut (dyn Write + 'a),
33 indent: String,
34 }
35
36 impl<'a> CodeWriter<'a> {
new(writer: &'a mut dyn Write) -> CodeWriter<'a>37 pub fn new(writer: &'a mut dyn Write) -> CodeWriter<'a> {
38 CodeWriter {
39 writer,
40 indent: "".to_string(),
41 }
42 }
43
write_line<S: AsRef<str>>(&mut self, line: S)44 pub fn write_line<S: AsRef<str>>(&mut self, line: S) {
45 (if line.as_ref().is_empty() {
46 self.writer.write_all(b"\n")
47 } else {
48 let s: String = [self.indent.as_ref(), line.as_ref(), "\n"].concat();
49 self.writer.write_all(s.as_bytes())
50 })
51 .unwrap();
52 }
53
write_generated(&mut self)54 pub fn write_generated(&mut self) {
55 self.write_line("// This file is generated. Do not edit");
56 self.write_generated_common();
57 }
58
write_generated_common(&mut self)59 fn write_generated_common(&mut self) {
60 // https://secure.phabricator.com/T784
61 self.write_line("// @generated");
62
63 self.write_line("");
64 self.comment("https://github.com/Manishearth/rust-clippy/issues/702");
65 self.write_line("#![allow(unknown_lints)]");
66 self.write_line("#![allow(clippy::all)]");
67 self.write_line("");
68 self.write_line("#![allow(box_pointers)]");
69 self.write_line("#![allow(dead_code)]");
70 self.write_line("#![allow(missing_docs)]");
71 self.write_line("#![allow(non_camel_case_types)]");
72 self.write_line("#![allow(non_snake_case)]");
73 self.write_line("#![allow(non_upper_case_globals)]");
74 self.write_line("#![allow(trivial_casts)]");
75 self.write_line("#![allow(unsafe_code)]");
76 self.write_line("#![allow(unused_imports)]");
77 self.write_line("#![allow(unused_results)]");
78 }
79
indented<F>(&mut self, cb: F) where F: Fn(&mut CodeWriter),80 pub fn indented<F>(&mut self, cb: F)
81 where
82 F: Fn(&mut CodeWriter),
83 {
84 cb(&mut CodeWriter {
85 writer: self.writer,
86 indent: format!("{} ", self.indent),
87 });
88 }
89
90 #[allow(dead_code)]
commented<F>(&mut self, cb: F) where F: Fn(&mut CodeWriter),91 pub fn commented<F>(&mut self, cb: F)
92 where
93 F: Fn(&mut CodeWriter),
94 {
95 cb(&mut CodeWriter {
96 writer: self.writer,
97 indent: format!("// {}", self.indent),
98 });
99 }
100
block<F>(&mut self, first_line: &str, last_line: &str, cb: F) where F: Fn(&mut CodeWriter),101 pub fn block<F>(&mut self, first_line: &str, last_line: &str, cb: F)
102 where
103 F: Fn(&mut CodeWriter),
104 {
105 self.write_line(first_line);
106 self.indented(cb);
107 self.write_line(last_line);
108 }
109
expr_block<F>(&mut self, prefix: &str, cb: F) where F: Fn(&mut CodeWriter),110 pub fn expr_block<F>(&mut self, prefix: &str, cb: F)
111 where
112 F: Fn(&mut CodeWriter),
113 {
114 self.block(&format!("{} {{", prefix), "}", cb);
115 }
116
impl_self_block<S: AsRef<str>, F>(&mut self, name: S, cb: F) where F: Fn(&mut CodeWriter),117 pub fn impl_self_block<S: AsRef<str>, F>(&mut self, name: S, cb: F)
118 where
119 F: Fn(&mut CodeWriter),
120 {
121 self.expr_block(&format!("impl {}", name.as_ref()), cb);
122 }
123
pub_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F) where F: Fn(&mut CodeWriter),124 pub fn pub_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F)
125 where
126 F: Fn(&mut CodeWriter),
127 {
128 self.expr_block(&format!("pub struct {}", name.as_ref()), cb);
129 }
130
pub_trait<F>(&mut self, name: &str, cb: F) where F: Fn(&mut CodeWriter),131 pub fn pub_trait<F>(&mut self, name: &str, cb: F)
132 where
133 F: Fn(&mut CodeWriter),
134 {
135 self.expr_block(&format!("pub trait {}", name), cb);
136 }
137
field_entry(&mut self, name: &str, value: &str)138 pub fn field_entry(&mut self, name: &str, value: &str) {
139 self.write_line(&format!("{}: {},", name, value));
140 }
141
field_decl(&mut self, name: &str, field_type: &str)142 pub fn field_decl(&mut self, name: &str, field_type: &str) {
143 self.write_line(&format!("{}: {},", name, field_type));
144 }
145
comment(&mut self, comment: &str)146 pub fn comment(&mut self, comment: &str) {
147 if comment.is_empty() {
148 self.write_line("//");
149 } else {
150 self.write_line(&format!("// {}", comment));
151 }
152 }
153
fn_def(&mut self, sig: &str)154 pub fn fn_def(&mut self, sig: &str) {
155 self.write_line(&format!("fn {};", sig));
156 }
157
fn_block<F>(&mut self, public: bool, sig: &str, cb: F) where F: Fn(&mut CodeWriter),158 pub fn fn_block<F>(&mut self, public: bool, sig: &str, cb: F)
159 where
160 F: Fn(&mut CodeWriter),
161 {
162 if public {
163 self.expr_block(&format!("pub fn {}", sig), cb);
164 } else {
165 self.expr_block(&format!("fn {}", sig), cb);
166 }
167 }
168
pub_fn<F>(&mut self, sig: &str, cb: F) where F: Fn(&mut CodeWriter),169 pub fn pub_fn<F>(&mut self, sig: &str, cb: F)
170 where
171 F: Fn(&mut CodeWriter),
172 {
173 self.fn_block(true, sig, cb);
174 }
175 }
176
177 use super::util::{self, fq_grpc, to_snake_case, MethodType};
178
179 struct MethodGen<'a> {
180 proto: &'a MethodDescriptorProto,
181 service_name: String,
182 service_path: String,
183 root_scope: &'a RootScope<'a>,
184 }
185
186 impl<'a> MethodGen<'a> {
new( proto: &'a MethodDescriptorProto, service_name: String, service_path: String, root_scope: &'a RootScope<'a>, ) -> MethodGen<'a>187 fn new(
188 proto: &'a MethodDescriptorProto,
189 service_name: String,
190 service_path: String,
191 root_scope: &'a RootScope<'a>,
192 ) -> MethodGen<'a> {
193 MethodGen {
194 proto,
195 service_name,
196 service_path,
197 root_scope,
198 }
199 }
200
input(&self) -> String201 fn input(&self) -> String {
202 format!(
203 "super::{}",
204 self.root_scope
205 .find_message(self.proto.get_input_type())
206 .rust_fq_name()
207 )
208 }
209
output(&self) -> String210 fn output(&self) -> String {
211 format!(
212 "super::{}",
213 self.root_scope
214 .find_message(self.proto.get_output_type())
215 .rust_fq_name()
216 )
217 }
218
method_type(&self) -> (MethodType, String)219 fn method_type(&self) -> (MethodType, String) {
220 match (
221 self.proto.get_client_streaming(),
222 self.proto.get_server_streaming(),
223 ) {
224 (false, false) => (MethodType::Unary, fq_grpc("MethodType::Unary")),
225 (true, false) => (
226 MethodType::ClientStreaming,
227 fq_grpc("MethodType::ClientStreaming"),
228 ),
229 (false, true) => (
230 MethodType::ServerStreaming,
231 fq_grpc("MethodType::ServerStreaming"),
232 ),
233 (true, true) => (MethodType::Duplex, fq_grpc("MethodType::Duplex")),
234 }
235 }
236
service_name(&self) -> String237 fn service_name(&self) -> String {
238 to_snake_case(&self.service_name)
239 }
240
name(&self) -> String241 fn name(&self) -> String {
242 to_snake_case(self.proto.get_name())
243 }
244
fq_name(&self) -> String245 fn fq_name(&self) -> String {
246 format!("\"{}/{}\"", self.service_path, &self.proto.get_name())
247 }
248
const_method_name(&self) -> String249 fn const_method_name(&self) -> String {
250 format!(
251 "METHOD_{}_{}",
252 self.service_name().to_uppercase(),
253 self.name().to_uppercase()
254 )
255 }
256
write_definition(&self, w: &mut CodeWriter)257 fn write_definition(&self, w: &mut CodeWriter) {
258 let head = format!(
259 "const {}: {}<{}, {}> = {} {{",
260 self.const_method_name(),
261 fq_grpc("Method"),
262 self.input(),
263 self.output(),
264 fq_grpc("Method")
265 );
266 let pb_mar = format!(
267 "{} {{ ser: {}, de: {} }}",
268 fq_grpc("Marshaller"),
269 fq_grpc("pb_ser"),
270 fq_grpc("pb_de")
271 );
272 w.block(&head, "};", |w| {
273 w.field_entry("ty", &self.method_type().1);
274 w.field_entry("name", &self.fq_name());
275 w.field_entry("req_mar", &pb_mar);
276 w.field_entry("resp_mar", &pb_mar);
277 });
278 }
279
280 // Method signatures
unary(&self, method_name: &str) -> String281 fn unary(&self, method_name: &str) -> String {
282 format!(
283 "{}(&self, req: &{}) -> {}<{}>",
284 method_name,
285 self.input(),
286 fq_grpc("Result"),
287 self.output()
288 )
289 }
290
unary_opt(&self, method_name: &str) -> String291 fn unary_opt(&self, method_name: &str) -> String {
292 format!(
293 "{}_opt(&self, req: &{}, opt: {}) -> {}<{}>",
294 method_name,
295 self.input(),
296 fq_grpc("CallOption"),
297 fq_grpc("Result"),
298 self.output()
299 )
300 }
301
unary_async(&self, method_name: &str) -> String302 fn unary_async(&self, method_name: &str) -> String {
303 format!(
304 "{}_async(&self, req: &{}) -> {}<{}<{}>>",
305 method_name,
306 self.input(),
307 fq_grpc("Result"),
308 fq_grpc("ClientUnaryReceiver"),
309 self.output()
310 )
311 }
312
unary_async_opt(&self, method_name: &str) -> String313 fn unary_async_opt(&self, method_name: &str) -> String {
314 format!(
315 "{}_async_opt(&self, req: &{}, opt: {}) -> {}<{}<{}>>",
316 method_name,
317 self.input(),
318 fq_grpc("CallOption"),
319 fq_grpc("Result"),
320 fq_grpc("ClientUnaryReceiver"),
321 self.output()
322 )
323 }
324
client_streaming(&self, method_name: &str) -> String325 fn client_streaming(&self, method_name: &str) -> String {
326 format!(
327 "{}(&self) -> {}<({}<{}>, {}<{}>)>",
328 method_name,
329 fq_grpc("Result"),
330 fq_grpc("ClientCStreamSender"),
331 self.input(),
332 fq_grpc("ClientCStreamReceiver"),
333 self.output()
334 )
335 }
336
client_streaming_opt(&self, method_name: &str) -> String337 fn client_streaming_opt(&self, method_name: &str) -> String {
338 format!(
339 "{}_opt(&self, opt: {}) -> {}<({}<{}>, {}<{}>)>",
340 method_name,
341 fq_grpc("CallOption"),
342 fq_grpc("Result"),
343 fq_grpc("ClientCStreamSender"),
344 self.input(),
345 fq_grpc("ClientCStreamReceiver"),
346 self.output()
347 )
348 }
349
server_streaming(&self, method_name: &str) -> String350 fn server_streaming(&self, method_name: &str) -> String {
351 format!(
352 "{}(&self, req: &{}) -> {}<{}<{}>>",
353 method_name,
354 self.input(),
355 fq_grpc("Result"),
356 fq_grpc("ClientSStreamReceiver"),
357 self.output()
358 )
359 }
360
server_streaming_opt(&self, method_name: &str) -> String361 fn server_streaming_opt(&self, method_name: &str) -> String {
362 format!(
363 "{}_opt(&self, req: &{}, opt: {}) -> {}<{}<{}>>",
364 method_name,
365 self.input(),
366 fq_grpc("CallOption"),
367 fq_grpc("Result"),
368 fq_grpc("ClientSStreamReceiver"),
369 self.output()
370 )
371 }
372
duplex_streaming(&self, method_name: &str) -> String373 fn duplex_streaming(&self, method_name: &str) -> String {
374 format!(
375 "{}(&self) -> {}<({}<{}>, {}<{}>)>",
376 method_name,
377 fq_grpc("Result"),
378 fq_grpc("ClientDuplexSender"),
379 self.input(),
380 fq_grpc("ClientDuplexReceiver"),
381 self.output()
382 )
383 }
384
duplex_streaming_opt(&self, method_name: &str) -> String385 fn duplex_streaming_opt(&self, method_name: &str) -> String {
386 format!(
387 "{}_opt(&self, opt: {}) -> {}<({}<{}>, {}<{}>)>",
388 method_name,
389 fq_grpc("CallOption"),
390 fq_grpc("Result"),
391 fq_grpc("ClientDuplexSender"),
392 self.input(),
393 fq_grpc("ClientDuplexReceiver"),
394 self.output()
395 )
396 }
397
write_client(&self, w: &mut CodeWriter)398 fn write_client(&self, w: &mut CodeWriter) {
399 let method_name = self.name();
400 match self.method_type().0 {
401 // Unary
402 MethodType::Unary => {
403 w.pub_fn(&self.unary_opt(&method_name), |w| {
404 w.write_line(&format!(
405 "self.client.unary_call(&{}, req, opt)",
406 self.const_method_name()
407 ));
408 });
409 w.write_line("");
410
411 w.pub_fn(&self.unary(&method_name), |w| {
412 w.write_line(&format!(
413 "self.{}_opt(req, {})",
414 method_name,
415 fq_grpc("CallOption::default()")
416 ));
417 });
418 w.write_line("");
419
420 w.pub_fn(&self.unary_async_opt(&method_name), |w| {
421 w.write_line(&format!(
422 "self.client.unary_call_async(&{}, req, opt)",
423 self.const_method_name()
424 ));
425 });
426 w.write_line("");
427
428 w.pub_fn(&self.unary_async(&method_name), |w| {
429 w.write_line(&format!(
430 "self.{}_async_opt(req, {})",
431 method_name,
432 fq_grpc("CallOption::default()")
433 ));
434 });
435 }
436
437 // Client streaming
438 MethodType::ClientStreaming => {
439 w.pub_fn(&self.client_streaming_opt(&method_name), |w| {
440 w.write_line(&format!(
441 "self.client.client_streaming(&{}, opt)",
442 self.const_method_name()
443 ));
444 });
445 w.write_line("");
446
447 w.pub_fn(&self.client_streaming(&method_name), |w| {
448 w.write_line(&format!(
449 "self.{}_opt({})",
450 method_name,
451 fq_grpc("CallOption::default()")
452 ));
453 });
454 }
455
456 // Server streaming
457 MethodType::ServerStreaming => {
458 w.pub_fn(&self.server_streaming_opt(&method_name), |w| {
459 w.write_line(&format!(
460 "self.client.server_streaming(&{}, req, opt)",
461 self.const_method_name()
462 ));
463 });
464 w.write_line("");
465
466 w.pub_fn(&self.server_streaming(&method_name), |w| {
467 w.write_line(&format!(
468 "self.{}_opt(req, {})",
469 method_name,
470 fq_grpc("CallOption::default()")
471 ));
472 });
473 }
474
475 // Duplex streaming
476 MethodType::Duplex => {
477 w.pub_fn(&self.duplex_streaming_opt(&method_name), |w| {
478 w.write_line(&format!(
479 "self.client.duplex_streaming(&{}, opt)",
480 self.const_method_name()
481 ));
482 });
483 w.write_line("");
484
485 w.pub_fn(&self.duplex_streaming(&method_name), |w| {
486 w.write_line(&format!(
487 "self.{}_opt({})",
488 method_name,
489 fq_grpc("CallOption::default()")
490 ));
491 });
492 }
493 };
494 }
495
write_service(&self, w: &mut CodeWriter)496 fn write_service(&self, w: &mut CodeWriter) {
497 let req_stream_type = format!("{}<{}>", fq_grpc("RequestStream"), self.input());
498 let (req, req_type, resp_type) = match self.method_type().0 {
499 MethodType::Unary => ("req", self.input(), "UnarySink"),
500 MethodType::ClientStreaming => ("stream", req_stream_type, "ClientStreamingSink"),
501 MethodType::ServerStreaming => ("req", self.input(), "ServerStreamingSink"),
502 MethodType::Duplex => ("stream", req_stream_type, "DuplexSink"),
503 };
504 let sig = format!(
505 "{}(&mut self, ctx: {}, {}: {}, sink: {}<{}>)",
506 self.name(),
507 fq_grpc("RpcContext"),
508 req,
509 req_type,
510 fq_grpc(resp_type),
511 self.output()
512 );
513 w.fn_def(&sig);
514 }
515
write_bind(&self, w: &mut CodeWriter)516 fn write_bind(&self, w: &mut CodeWriter) {
517 let add = match self.method_type().0 {
518 MethodType::Unary => "add_unary_handler",
519 MethodType::ClientStreaming => "add_client_streaming_handler",
520 MethodType::ServerStreaming => "add_server_streaming_handler",
521 MethodType::Duplex => "add_duplex_streaming_handler",
522 };
523 w.block(
524 &format!(
525 "builder = builder.{}(&{}, move |ctx, req, resp| {{",
526 add,
527 self.const_method_name()
528 ),
529 "});",
530 |w| {
531 w.write_line(&format!("instance.{}(ctx, req, resp)", self.name()));
532 },
533 );
534 }
535 }
536
537 struct ServiceGen<'a> {
538 proto: &'a ServiceDescriptorProto,
539 methods: Vec<MethodGen<'a>>,
540 }
541
542 impl<'a> ServiceGen<'a> {
new( proto: &'a ServiceDescriptorProto, file: &FileDescriptorProto, root_scope: &'a RootScope, ) -> ServiceGen<'a>543 fn new(
544 proto: &'a ServiceDescriptorProto,
545 file: &FileDescriptorProto,
546 root_scope: &'a RootScope,
547 ) -> ServiceGen<'a> {
548 let service_path = if file.get_package().is_empty() {
549 format!("/{}", proto.get_name())
550 } else {
551 format!("/{}.{}", file.get_package(), proto.get_name())
552 };
553 let methods = proto
554 .get_method()
555 .iter()
556 .map(|m| {
557 MethodGen::new(
558 m,
559 util::to_camel_case(proto.get_name()),
560 service_path.clone(),
561 root_scope,
562 )
563 })
564 .collect();
565
566 ServiceGen { proto, methods }
567 }
568
service_name(&self) -> String569 fn service_name(&self) -> String {
570 util::to_camel_case(self.proto.get_name())
571 }
572
client_name(&self) -> String573 fn client_name(&self) -> String {
574 format!("{}Client", self.service_name())
575 }
576
write_client(&self, w: &mut CodeWriter)577 fn write_client(&self, w: &mut CodeWriter) {
578 w.write_line("#[derive(Clone)]");
579 w.pub_struct(&self.client_name(), |w| {
580 w.field_decl("client", "::grpcio::Client");
581 });
582
583 w.write_line("");
584
585 w.impl_self_block(&self.client_name(), |w| {
586 w.pub_fn("new(channel: ::grpcio::Channel) -> Self", |w| {
587 w.expr_block(&self.client_name(), |w| {
588 w.field_entry("client", "::grpcio::Client::new(channel)");
589 });
590 });
591
592 for method in &self.methods {
593 w.write_line("");
594 method.write_client(w);
595 }
596 w.pub_fn(
597 "spawn<F>(&self, f: F) where F: ::futures::Future<Output = ()> + Send + 'static",
598 |w| {
599 w.write_line("self.client.spawn(f)");
600 },
601 )
602 });
603 }
604
write_server(&self, w: &mut CodeWriter)605 fn write_server(&self, w: &mut CodeWriter) {
606 w.pub_trait(&self.service_name(), |w| {
607 for method in &self.methods {
608 method.write_service(w);
609 }
610 });
611
612 w.write_line("");
613
614 let s = format!(
615 "create_{}<S: {} + Send + Clone + 'static>(s: S) -> {}",
616 to_snake_case(&self.service_name()),
617 self.service_name(),
618 fq_grpc("Service")
619 );
620 w.pub_fn(&s, |w| {
621 w.write_line("let mut builder = ::grpcio::ServiceBuilder::new();");
622 for method in &self.methods[0..self.methods.len() - 1] {
623 w.write_line("let mut instance = s.clone();");
624 method.write_bind(w);
625 }
626
627 w.write_line("let mut instance = s;");
628 self.methods[self.methods.len() - 1].write_bind(w);
629
630 w.write_line("builder.build()");
631 });
632 }
633
write_method_definitions(&self, w: &mut CodeWriter)634 fn write_method_definitions(&self, w: &mut CodeWriter) {
635 for (i, method) in self.methods.iter().enumerate() {
636 if i != 0 {
637 w.write_line("");
638 }
639
640 method.write_definition(w);
641 }
642 }
643
write(&self, w: &mut CodeWriter)644 fn write(&self, w: &mut CodeWriter) {
645 self.write_method_definitions(w);
646 w.write_line("");
647 self.write_client(w);
648 w.write_line("");
649 self.write_server(w);
650 }
651 }
652
gen_file( file: &FileDescriptorProto, root_scope: &RootScope, ) -> Option<compiler_plugin::GenResult>653 fn gen_file(
654 file: &FileDescriptorProto,
655 root_scope: &RootScope,
656 ) -> Option<compiler_plugin::GenResult> {
657 if file.get_service().is_empty() {
658 return None;
659 }
660
661 let base = protobuf::descriptorx::proto_path_to_rust_mod(file.get_name());
662
663 let mut v = Vec::new();
664 {
665 let mut w = CodeWriter::new(&mut v);
666 w.write_generated();
667
668 for service in file.get_service() {
669 w.write_line("");
670 ServiceGen::new(service, file, root_scope).write(&mut w);
671 }
672 }
673
674 Some(compiler_plugin::GenResult {
675 name: base + "_grpc.rs",
676 content: v,
677 })
678 }
679
gen( file_descriptors: &[FileDescriptorProto], files_to_generate: &[String], ) -> Vec<compiler_plugin::GenResult>680 pub fn gen(
681 file_descriptors: &[FileDescriptorProto],
682 files_to_generate: &[String],
683 ) -> Vec<compiler_plugin::GenResult> {
684 let files_map: HashMap<&str, &FileDescriptorProto> =
685 file_descriptors.iter().map(|f| (f.get_name(), f)).collect();
686
687 let root_scope = RootScope { file_descriptors };
688
689 let mut results = Vec::new();
690
691 for file_name in files_to_generate {
692 let file = files_map[&file_name[..]];
693
694 if file.get_service().is_empty() {
695 continue;
696 }
697
698 results.extend(gen_file(file, &root_scope).into_iter());
699 }
700
701 results
702 }
703
protoc_gen_grpc_rust_main()704 pub fn protoc_gen_grpc_rust_main() {
705 compiler_plugin::plugin_main(gen);
706 }
707