xref: /tonic/tonic-build/src/prost.rs (revision f4a879db)
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