1 use crate::{code_gen::CodeGenBuilder, compile_settings::CompileSettings};
2
3 use super::Attributes;
4 use proc_macro2::TokenStream;
5 use prost_build::{Config, Method, Service};
6 use quote::ToTokens;
7 use std::{
8 collections::HashSet,
9 ffi::OsString,
10 io,
11 path::{Path, PathBuf},
12 };
13
14 /// Configure `tonic-build` code generation.
15 ///
16 /// Use [`compile_protos`] instead if you don't need to tweak anything.
configure() -> Builder17 pub fn configure() -> Builder {
18 Builder {
19 build_client: true,
20 build_server: true,
21 build_transport: true,
22 file_descriptor_set_path: None,
23 skip_protoc_run: false,
24 out_dir: None,
25 extern_path: Vec::new(),
26 field_attributes: Vec::new(),
27 message_attributes: Vec::new(),
28 enum_attributes: Vec::new(),
29 type_attributes: Vec::new(),
30 boxed: Vec::new(),
31 btree_map: None,
32 bytes: None,
33 server_attributes: Attributes::default(),
34 client_attributes: Attributes::default(),
35 proto_path: "super".to_string(),
36 compile_well_known_types: false,
37 emit_package: true,
38 protoc_args: Vec::new(),
39 include_file: None,
40 emit_rerun_if_changed: std::env::var_os("CARGO").is_some(),
41 disable_comments: HashSet::default(),
42 use_arc_self: false,
43 generate_default_stubs: false,
44 compile_settings: CompileSettings::default(),
45 skip_debug: HashSet::default(),
46 }
47 }
48
49 /// Simple `.proto` compiling. Use [`configure`] instead if you need more options.
50 ///
51 /// The include directory will be the parent folder of the specified path.
52 /// The package name will be the filename without the extension.
compile_protos(proto: impl AsRef<Path>) -> io::Result<()>53 pub fn compile_protos(proto: impl AsRef<Path>) -> io::Result<()> {
54 let proto_path: &Path = proto.as_ref();
55
56 // directory the main .proto file resides in
57 let proto_dir = proto_path
58 .parent()
59 .expect("proto file should reside in a directory");
60
61 self::configure().compile_protos(&[proto_path], &[proto_dir])
62 }
63
64 /// Simple file descriptor set compiling. Use [`configure`] instead if you need more options.
compile_fds(fds: prost_types::FileDescriptorSet) -> io::Result<()>65 pub fn compile_fds(fds: prost_types::FileDescriptorSet) -> io::Result<()> {
66 self::configure().compile_fds(fds)
67 }
68
69 /// Non-path Rust types allowed for request/response types.
70 const NON_PATH_TYPE_ALLOWLIST: &[&str] = &["()"];
71
72 /// Newtype wrapper for prost to add tonic-specific extensions
73 struct TonicBuildService {
74 prost_service: Service,
75 methods: Vec<TonicBuildMethod>,
76 }
77
78 impl TonicBuildService {
new(prost_service: Service, settings: CompileSettings) -> Self79 fn new(prost_service: Service, settings: CompileSettings) -> Self {
80 Self {
81 // CompileSettings are currently only consumed method-by-method but if you need them in the Service, here's your spot.
82 // The tonic_build::Service trait specifies that methods are borrowed, so they have to reified up front.
83 methods: prost_service
84 .methods
85 .iter()
86 .map(|prost_method| TonicBuildMethod {
87 prost_method: prost_method.clone(),
88 settings: settings.clone(),
89 })
90 .collect(),
91 prost_service,
92 }
93 }
94 }
95
96 /// Newtype wrapper for prost to add tonic-specific extensions
97 struct TonicBuildMethod {
98 prost_method: Method,
99 settings: CompileSettings,
100 }
101
102 impl crate::Service for TonicBuildService {
103 type Method = TonicBuildMethod;
104 type Comment = String;
105
name(&self) -> &str106 fn name(&self) -> &str {
107 &self.prost_service.name
108 }
109
package(&self) -> &str110 fn package(&self) -> &str {
111 &self.prost_service.package
112 }
113
identifier(&self) -> &str114 fn identifier(&self) -> &str {
115 &self.prost_service.proto_name
116 }
117
comment(&self) -> &[Self::Comment]118 fn comment(&self) -> &[Self::Comment] {
119 &self.prost_service.comments.leading[..]
120 }
121
methods(&self) -> &[Self::Method]122 fn methods(&self) -> &[Self::Method] {
123 &self.methods
124 }
125 }
126
127 impl crate::Method for TonicBuildMethod {
128 type Comment = String;
129
name(&self) -> &str130 fn name(&self) -> &str {
131 &self.prost_method.name
132 }
133
identifier(&self) -> &str134 fn identifier(&self) -> &str {
135 &self.prost_method.proto_name
136 }
137
138 /// For code generation, you can override the codec.
139 ///
140 /// You should set the codec path to an import path that has a free
141 /// function like `fn default()`. The default value is tonic::codec::ProstCodec,
142 /// which returns a default-configured ProstCodec. You may wish to configure
143 /// the codec, e.g., with a buffer configuration.
144 ///
145 /// Though ProstCodec implements Default, it is currently only required that
146 /// the function match the Default trait's function spec.
codec_path(&self) -> &str147 fn codec_path(&self) -> &str {
148 &self.settings.codec_path
149 }
150
client_streaming(&self) -> bool151 fn client_streaming(&self) -> bool {
152 self.prost_method.client_streaming
153 }
154
server_streaming(&self) -> bool155 fn server_streaming(&self) -> bool {
156 self.prost_method.server_streaming
157 }
158
comment(&self) -> &[Self::Comment]159 fn comment(&self) -> &[Self::Comment] {
160 &self.prost_method.comments.leading[..]
161 }
162
deprecated(&self) -> bool163 fn deprecated(&self) -> bool {
164 self.prost_method.options.deprecated.unwrap_or_default()
165 }
166
request_response_name( &self, proto_path: &str, compile_well_known_types: bool, ) -> (TokenStream, TokenStream)167 fn request_response_name(
168 &self,
169 proto_path: &str,
170 compile_well_known_types: bool,
171 ) -> (TokenStream, TokenStream) {
172 let convert_type = |proto_type: &str, rust_type: &str| -> TokenStream {
173 if (is_google_type(proto_type) && !compile_well_known_types)
174 || rust_type.starts_with("::")
175 || NON_PATH_TYPE_ALLOWLIST.iter().any(|ty| *ty == rust_type)
176 {
177 rust_type.parse::<TokenStream>().unwrap()
178 } else if rust_type.starts_with("crate::") {
179 syn::parse_str::<syn::Path>(rust_type)
180 .unwrap()
181 .to_token_stream()
182 } else {
183 syn::parse_str::<syn::Path>(&format!("{}::{}", proto_path, rust_type))
184 .unwrap()
185 .to_token_stream()
186 }
187 };
188
189 let request = convert_type(
190 &self.prost_method.input_proto_type,
191 &self.prost_method.input_type,
192 );
193 let response = convert_type(
194 &self.prost_method.output_proto_type,
195 &self.prost_method.output_type,
196 );
197 (request, response)
198 }
199 }
200
is_google_type(ty: &str) -> bool201 fn is_google_type(ty: &str) -> bool {
202 ty.starts_with(".google.protobuf")
203 }
204
205 struct ServiceGenerator {
206 builder: Builder,
207 clients: TokenStream,
208 servers: TokenStream,
209 }
210
211 impl ServiceGenerator {
new(builder: Builder) -> Self212 fn new(builder: Builder) -> Self {
213 ServiceGenerator {
214 builder,
215 clients: TokenStream::default(),
216 servers: TokenStream::default(),
217 }
218 }
219 }
220
221 impl prost_build::ServiceGenerator for ServiceGenerator {
generate(&mut self, service: prost_build::Service, _buf: &mut String)222 fn generate(&mut self, service: prost_build::Service, _buf: &mut String) {
223 if self.builder.build_server {
224 let server = CodeGenBuilder::new()
225 .emit_package(self.builder.emit_package)
226 .compile_well_known_types(self.builder.compile_well_known_types)
227 .attributes(self.builder.server_attributes.clone())
228 .disable_comments(self.builder.disable_comments.clone())
229 .use_arc_self(self.builder.use_arc_self)
230 .generate_default_stubs(self.builder.generate_default_stubs)
231 .generate_server(
232 &TonicBuildService::new(service.clone(), self.builder.compile_settings.clone()),
233 &self.builder.proto_path,
234 );
235
236 self.servers.extend(server);
237 }
238
239 if self.builder.build_client {
240 let client = CodeGenBuilder::new()
241 .emit_package(self.builder.emit_package)
242 .compile_well_known_types(self.builder.compile_well_known_types)
243 .attributes(self.builder.client_attributes.clone())
244 .disable_comments(self.builder.disable_comments.clone())
245 .build_transport(self.builder.build_transport)
246 .generate_client(
247 &TonicBuildService::new(service, self.builder.compile_settings.clone()),
248 &self.builder.proto_path,
249 );
250
251 self.clients.extend(client);
252 }
253 }
254
finalize(&mut self, buf: &mut String)255 fn finalize(&mut self, buf: &mut String) {
256 if self.builder.build_client && !self.clients.is_empty() {
257 let clients = &self.clients;
258
259 let client_service = quote::quote! {
260 #clients
261 };
262
263 let ast: syn::File = syn::parse2(client_service).expect("not a valid tokenstream");
264 let code = prettyplease::unparse(&ast);
265 buf.push_str(&code);
266
267 self.clients = TokenStream::default();
268 }
269
270 if self.builder.build_server && !self.servers.is_empty() {
271 let servers = &self.servers;
272
273 let server_service = quote::quote! {
274 #servers
275 };
276
277 let ast: syn::File = syn::parse2(server_service).expect("not a valid tokenstream");
278 let code = prettyplease::unparse(&ast);
279 buf.push_str(&code);
280
281 self.servers = TokenStream::default();
282 }
283 }
284 }
285
286 /// Service generator builder.
287 #[derive(Debug, Clone)]
288 pub struct Builder {
289 pub(crate) build_client: bool,
290 pub(crate) build_server: bool,
291 pub(crate) build_transport: bool,
292 pub(crate) file_descriptor_set_path: Option<PathBuf>,
293 pub(crate) skip_protoc_run: bool,
294 pub(crate) extern_path: Vec<(String, String)>,
295 pub(crate) field_attributes: Vec<(String, String)>,
296 pub(crate) type_attributes: Vec<(String, String)>,
297 pub(crate) message_attributes: Vec<(String, String)>,
298 pub(crate) enum_attributes: Vec<(String, String)>,
299 pub(crate) boxed: Vec<String>,
300 pub(crate) btree_map: Option<Vec<String>>,
301 pub(crate) bytes: Option<Vec<String>>,
302 pub(crate) server_attributes: Attributes,
303 pub(crate) client_attributes: Attributes,
304 pub(crate) proto_path: String,
305 pub(crate) emit_package: bool,
306 pub(crate) compile_well_known_types: bool,
307 pub(crate) protoc_args: Vec<OsString>,
308 pub(crate) include_file: Option<PathBuf>,
309 pub(crate) emit_rerun_if_changed: bool,
310 pub(crate) disable_comments: HashSet<String>,
311 pub(crate) use_arc_self: bool,
312 pub(crate) generate_default_stubs: bool,
313 pub(crate) compile_settings: CompileSettings,
314 pub(crate) skip_debug: HashSet<String>,
315
316 out_dir: Option<PathBuf>,
317 }
318
319 impl Builder {
320 /// Enable or disable gRPC client code generation.
build_client(mut self, enable: bool) -> Self321 pub fn build_client(mut self, enable: bool) -> Self {
322 self.build_client = enable;
323 self
324 }
325
326 /// Enable or disable gRPC server code generation.
build_server(mut self, enable: bool) -> Self327 pub fn build_server(mut self, enable: bool) -> Self {
328 self.build_server = enable;
329 self
330 }
331
332 /// Enable or disable generated clients and servers to have built-in tonic
333 /// transport features.
334 ///
335 /// When the `transport` feature is disabled this does nothing.
build_transport(mut self, enable: bool) -> Self336 pub fn build_transport(mut self, enable: bool) -> Self {
337 self.build_transport = enable;
338 self
339 }
340
341 /// Generate a file containing the encoded `prost_types::FileDescriptorSet` for protocol buffers
342 /// modules. This is required for implementing gRPC Server Reflection.
file_descriptor_set_path(mut self, path: impl AsRef<Path>) -> Self343 pub fn file_descriptor_set_path(mut self, path: impl AsRef<Path>) -> Self {
344 self.file_descriptor_set_path = Some(path.as_ref().to_path_buf());
345 self
346 }
347
348 /// In combination with with file_descriptor_set_path, this can be used to provide a file
349 /// descriptor set as an input file, rather than having prost-build generate the file by
350 /// calling protoc.
skip_protoc_run(mut self) -> Self351 pub fn skip_protoc_run(mut self) -> Self {
352 self.skip_protoc_run = true;
353 self
354 }
355
356 /// Set the output directory to generate code to.
357 ///
358 /// Defaults to the `OUT_DIR` environment variable.
out_dir(mut self, out_dir: impl AsRef<Path>) -> Self359 pub fn out_dir(mut self, out_dir: impl AsRef<Path>) -> Self {
360 self.out_dir = Some(out_dir.as_ref().to_path_buf());
361 self
362 }
363
364 /// Declare externally provided Protobuf package or type.
365 ///
366 /// Passed directly to `prost_build::Config.extern_path`.
367 /// Note that both the Protobuf path and the rust package paths should both be fully qualified.
368 /// i.e. Protobuf paths should start with "." and rust paths should start with "::"
extern_path(mut self, proto_path: impl AsRef<str>, rust_path: impl AsRef<str>) -> Self369 pub fn extern_path(mut self, proto_path: impl AsRef<str>, rust_path: impl AsRef<str>) -> Self {
370 self.extern_path.push((
371 proto_path.as_ref().to_string(),
372 rust_path.as_ref().to_string(),
373 ));
374 self
375 }
376
377 /// Add additional attribute to matched messages, enums, and one-offs.
378 ///
379 /// Passed directly to `prost_build::Config.field_attribute`.
field_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self380 pub fn field_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self {
381 self.field_attributes
382 .push((path.as_ref().to_string(), attribute.as_ref().to_string()));
383 self
384 }
385
386 /// Add additional attribute to matched messages, enums, and one-offs.
387 ///
388 /// Passed directly to `prost_build::Config.type_attribute`.
type_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self389 pub fn type_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self {
390 self.type_attributes
391 .push((path.as_ref().to_string(), attribute.as_ref().to_string()));
392 self
393 }
394
395 /// Add additional attribute to matched messages.
396 ///
397 /// Passed directly to `prost_build::Config.message_attribute`.
message_attribute<P: AsRef<str>, A: AsRef<str>>( mut self, path: P, attribute: A, ) -> Self398 pub fn message_attribute<P: AsRef<str>, A: AsRef<str>>(
399 mut self,
400 path: P,
401 attribute: A,
402 ) -> Self {
403 self.message_attributes
404 .push((path.as_ref().to_string(), attribute.as_ref().to_string()));
405 self
406 }
407
408 /// Add additional attribute to matched enums.
409 ///
410 /// Passed directly to `prost_build::Config.enum_attribute`.
enum_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self411 pub fn enum_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self {
412 self.enum_attributes
413 .push((path.as_ref().to_string(), attribute.as_ref().to_string()));
414 self
415 }
416
417 /// Add additional boxed fields.
418 ///
419 /// Passed directly to `prost_build::Config.boxed`.
boxed<P: AsRef<str>>(mut self, path: P) -> Self420 pub fn boxed<P: AsRef<str>>(mut self, path: P) -> Self {
421 self.boxed.push(path.as_ref().to_string());
422 self
423 }
424
425 /// Configure the code generator to generate Rust `BTreeMap` fields for Protobuf `map` type
426 /// fields.
427 ///
428 /// Passed directly to `prost_build::Config.btree_map`.
429 ///
430 /// Note: previous configured paths for `btree_map` will be cleared.
btree_map<I, S>(mut self, paths: I) -> Self where I: IntoIterator<Item = S>, S: AsRef<str>,431 pub fn btree_map<I, S>(mut self, paths: I) -> Self
432 where
433 I: IntoIterator<Item = S>,
434 S: AsRef<str>,
435 {
436 self.btree_map = Some(
437 paths
438 .into_iter()
439 .map(|path| path.as_ref().to_string())
440 .collect(),
441 );
442 self
443 }
444
445 /// Configure the code generator to generate Rust `bytes::Bytes` fields for Protobuf `bytes`
446 /// type fields.
447 ///
448 /// Passed directly to `prost_build::Config.bytes`.
449 ///
450 /// Note: previous configured paths for `bytes` will be cleared.
bytes<I, S>(mut self, paths: I) -> Self where I: IntoIterator<Item = S>, S: AsRef<str>,451 pub fn bytes<I, S>(mut self, paths: I) -> Self
452 where
453 I: IntoIterator<Item = S>,
454 S: AsRef<str>,
455 {
456 self.bytes = Some(
457 paths
458 .into_iter()
459 .map(|path| path.as_ref().to_string())
460 .collect(),
461 );
462 self
463 }
464
465 /// Add additional attribute to matched server `mod`s. Matches on the package name.
server_mod_attribute<P: AsRef<str>, A: AsRef<str>>( mut self, path: P, attribute: A, ) -> Self466 pub fn server_mod_attribute<P: AsRef<str>, A: AsRef<str>>(
467 mut self,
468 path: P,
469 attribute: A,
470 ) -> Self {
471 self.server_attributes
472 .push_mod(path.as_ref().to_string(), attribute.as_ref().to_string());
473 self
474 }
475
476 /// Add additional attribute to matched service servers. Matches on the service name.
server_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self477 pub fn server_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self {
478 self.server_attributes
479 .push_struct(path.as_ref().to_string(), attribute.as_ref().to_string());
480 self
481 }
482
483 /// Add additional attribute to matched client `mod`s. Matches on the package name.
client_mod_attribute<P: AsRef<str>, A: AsRef<str>>( mut self, path: P, attribute: A, ) -> Self484 pub fn client_mod_attribute<P: AsRef<str>, A: AsRef<str>>(
485 mut self,
486 path: P,
487 attribute: A,
488 ) -> Self {
489 self.client_attributes
490 .push_mod(path.as_ref().to_string(), attribute.as_ref().to_string());
491 self
492 }
493
494 /// Add additional attribute to matched service clients. Matches on the service name.
client_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self495 pub fn client_attribute<P: AsRef<str>, A: AsRef<str>>(mut self, path: P, attribute: A) -> Self {
496 self.client_attributes
497 .push_struct(path.as_ref().to_string(), attribute.as_ref().to_string());
498 self
499 }
500
501 /// Set the path to where tonic will search for the Request/Response proto structs
502 /// live relative to the module where you call `include_proto!`.
503 ///
504 /// This defaults to `super` since tonic will generate code in a module.
proto_path(mut self, proto_path: impl AsRef<str>) -> Self505 pub fn proto_path(mut self, proto_path: impl AsRef<str>) -> Self {
506 self.proto_path = proto_path.as_ref().to_string();
507 self
508 }
509
510 /// Configure Prost `protoc_args` build arguments.
511 ///
512 /// Note: Enabling `--experimental_allow_proto3_optional` requires protobuf >= 3.12.
protoc_arg<A: AsRef<str>>(mut self, arg: A) -> Self513 pub fn protoc_arg<A: AsRef<str>>(mut self, arg: A) -> Self {
514 self.protoc_args.push(arg.as_ref().into());
515 self
516 }
517
518 /// Disable service and rpc comments emission.
disable_comments(mut self, path: impl AsRef<str>) -> Self519 pub fn disable_comments(mut self, path: impl AsRef<str>) -> Self {
520 self.disable_comments.insert(path.as_ref().to_string());
521 self
522 }
523
524 /// Emit `Arc<Self>` receiver type in server traits instead of `&self`.
use_arc_self(mut self, enable: bool) -> Self525 pub fn use_arc_self(mut self, enable: bool) -> Self {
526 self.use_arc_self = enable;
527 self
528 }
529
530 /// Emits GRPC endpoints with no attached package. Effectively ignores protofile package declaration from grpc context.
531 ///
532 /// This effectively sets prost's exported package to an empty string.
disable_package_emission(mut self) -> Self533 pub fn disable_package_emission(mut self) -> Self {
534 self.emit_package = false;
535 self
536 }
537
538 /// Enable or disable directing Prost to compile well-known protobuf types instead
539 /// of using the already-compiled versions available in the `prost-types` crate.
540 ///
541 /// This defaults to `false`.
compile_well_known_types(mut self, compile_well_known_types: bool) -> Self542 pub fn compile_well_known_types(mut self, compile_well_known_types: bool) -> Self {
543 self.compile_well_known_types = compile_well_known_types;
544 self
545 }
546
547 /// Configures the optional module filename for easy inclusion of all generated Rust files
548 ///
549 /// If set, generates a file (inside the `OUT_DIR` or `out_dir()` as appropriate) which contains
550 /// a set of `pub mod XXX` statements combining to load all Rust files generated. This can allow
551 /// for a shortcut where multiple related proto files have been compiled together resulting in
552 /// a semi-complex set of includes.
include_file(mut self, path: impl AsRef<Path>) -> Self553 pub fn include_file(mut self, path: impl AsRef<Path>) -> Self {
554 self.include_file = Some(path.as_ref().to_path_buf());
555 self
556 }
557
558 /// Enable or disable emitting
559 /// [`cargo:rerun-if-changed=PATH`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed)
560 /// instructions for Cargo.
561 ///
562 /// If set, writes instructions to `stdout` for Cargo so that it understands
563 /// when to rerun the build script. By default, this setting is enabled if
564 /// the `CARGO` environment variable is set. The `CARGO` environment
565 /// variable is set by Cargo for build scripts. Therefore, this setting
566 /// should be enabled automatically when run from a build script. However,
567 /// the method of detection is not completely reliable since the `CARGO`
568 /// environment variable can have been set by anything else. If writing the
569 /// instructions to `stdout` is undesirable, you can disable this setting
570 /// explicitly.
emit_rerun_if_changed(mut self, enable: bool) -> Self571 pub fn emit_rerun_if_changed(mut self, enable: bool) -> Self {
572 self.emit_rerun_if_changed = enable;
573 self
574 }
575
576 /// Enable or disable directing service generation to providing a default implementation for service methods.
577 /// When this is false all gRPC methods must be explicitly implemented.
578 /// When this is true any unimplemented service methods will return 'unimplemented' gRPC error code.
579 /// When this is true all streaming server request RPC types explicitly use tonic::codegen::BoxStream type.
580 ///
581 /// This defaults to `false`.
generate_default_stubs(mut self, enable: bool) -> Self582 pub fn generate_default_stubs(mut self, enable: bool) -> Self {
583 self.generate_default_stubs = enable;
584 self
585 }
586
587 /// Override the default codec.
588 ///
589 /// If set, writes `{codec_path}::default()` in generated code wherever a codec is created.
590 ///
591 /// This defaults to `"tonic::codec::ProstCodec"`
codec_path(mut self, codec_path: impl Into<String>) -> Self592 pub fn codec_path(mut self, codec_path: impl Into<String>) -> Self {
593 self.compile_settings.codec_path = codec_path.into();
594 self
595 }
596
597 /// Skips generating `impl Debug` for types
skip_debug(mut self, path: impl AsRef<str>) -> Self598 pub fn skip_debug(mut self, path: impl AsRef<str>) -> Self {
599 self.skip_debug.insert(path.as_ref().to_string());
600 self
601 }
602
603 /// Compile the .proto files and execute code generation.
compile_protos( self, protos: &[impl AsRef<Path>], includes: &[impl AsRef<Path>], ) -> io::Result<()>604 pub fn compile_protos(
605 self,
606 protos: &[impl AsRef<Path>],
607 includes: &[impl AsRef<Path>],
608 ) -> io::Result<()> {
609 self.compile_protos_with_config(Config::new(), protos, includes)
610 }
611
612 /// Compile the .proto files and execute code generation using a custom
613 /// `prost_build::Config`. The provided config will be updated with this builder's config.
compile_protos_with_config( self, mut config: Config, protos: &[impl AsRef<Path>], includes: &[impl AsRef<Path>], ) -> io::Result<()>614 pub fn compile_protos_with_config(
615 self,
616 mut config: Config,
617 protos: &[impl AsRef<Path>],
618 includes: &[impl AsRef<Path>],
619 ) -> io::Result<()> {
620 if self.emit_rerun_if_changed {
621 for path in protos.iter() {
622 println!("cargo:rerun-if-changed={}", path.as_ref().display())
623 }
624
625 for path in includes.iter() {
626 // Cargo will watch the **entire** directory recursively. If we
627 // could figure out which files are imported by our protos we
628 // could specify only those files instead.
629 println!("cargo:rerun-if-changed={}", path.as_ref().display())
630 }
631 }
632
633 self.setup_config(&mut config);
634 config.compile_protos(protos, includes)
635 }
636
637 /// Execute code generation from a file descriptor set.
compile_fds(self, fds: prost_types::FileDescriptorSet) -> io::Result<()>638 pub fn compile_fds(self, fds: prost_types::FileDescriptorSet) -> io::Result<()> {
639 self.compile_fds_with_config(Config::new(), fds)
640 }
641
642 /// Execute code generation from a file descriptor set using a custom `prost_build::Config`.
compile_fds_with_config( self, mut config: Config, fds: prost_types::FileDescriptorSet, ) -> io::Result<()>643 pub fn compile_fds_with_config(
644 self,
645 mut config: Config,
646 fds: prost_types::FileDescriptorSet,
647 ) -> io::Result<()> {
648 self.setup_config(&mut config);
649 config.compile_fds(fds)
650 }
651
setup_config(self, config: &mut Config)652 fn setup_config(self, config: &mut Config) {
653 if let Some(out_dir) = self.out_dir.as_ref() {
654 config.out_dir(out_dir);
655 }
656 if let Some(path) = self.file_descriptor_set_path.as_ref() {
657 config.file_descriptor_set_path(path);
658 }
659 if self.skip_protoc_run {
660 config.skip_protoc_run();
661 }
662 for (proto_path, rust_path) in self.extern_path.iter() {
663 config.extern_path(proto_path, rust_path);
664 }
665 for (prost_path, attr) in self.field_attributes.iter() {
666 config.field_attribute(prost_path, attr);
667 }
668 for (prost_path, attr) in self.type_attributes.iter() {
669 config.type_attribute(prost_path, attr);
670 }
671 for (prost_path, attr) in self.message_attributes.iter() {
672 config.message_attribute(prost_path, attr);
673 }
674 for (prost_path, attr) in self.enum_attributes.iter() {
675 config.enum_attribute(prost_path, attr);
676 }
677 for prost_path in self.boxed.iter() {
678 config.boxed(prost_path);
679 }
680 if let Some(ref paths) = self.btree_map {
681 config.btree_map(paths);
682 }
683 if let Some(ref paths) = self.bytes {
684 config.bytes(paths);
685 }
686 if self.compile_well_known_types {
687 config.compile_well_known_types();
688 }
689 if let Some(path) = self.include_file.as_ref() {
690 config.include_file(path);
691 }
692 if !self.skip_debug.is_empty() {
693 config.skip_debug(&self.skip_debug);
694 }
695
696 for arg in self.protoc_args.iter() {
697 config.protoc_arg(arg);
698 }
699
700 config.service_generator(self.service_generator());
701 }
702
703 /// Turn the builder into a `ServiceGenerator` ready to be passed to `prost-build`s
704 /// `Config::service_generator`.
service_generator(self) -> Box<dyn prost_build::ServiceGenerator>705 pub fn service_generator(self) -> Box<dyn prost_build::ServiceGenerator> {
706 Box::new(ServiceGenerator::new(self))
707 }
708 }
709