prost_build/
config.rs

1use std::collections::HashMap;
2use std::default;
3use std::env;
4use std::ffi::{OsStr, OsString};
5use std::fmt;
6use std::fs;
7use std::io::{Error, ErrorKind, Result, Write};
8use std::path::{Path, PathBuf};
9use std::process::Command;
10
11use log::debug;
12use log::trace;
13
14use prost::Message;
15use prost_types::{FileDescriptorProto, FileDescriptorSet};
16
17use crate::code_generator::CodeGenerator;
18use crate::context::Context;
19use crate::extern_paths::ExternPaths;
20use crate::message_graph::MessageGraph;
21use crate::path::PathMap;
22use crate::BytesType;
23use crate::MapType;
24use crate::Module;
25use crate::ServiceGenerator;
26
27/// Configuration options for Protobuf code generation.
28///
29/// This configuration builder can be used to set non-default code generation options.
30pub struct Config {
31    pub(crate) file_descriptor_set_path: Option<PathBuf>,
32    pub(crate) service_generator: Option<Box<dyn ServiceGenerator>>,
33    pub(crate) map_type: PathMap<MapType>,
34    pub(crate) bytes_type: PathMap<BytesType>,
35    pub(crate) type_attributes: PathMap<String>,
36    pub(crate) message_attributes: PathMap<String>,
37    pub(crate) enum_attributes: PathMap<String>,
38    pub(crate) field_attributes: PathMap<String>,
39    pub(crate) boxed: PathMap<()>,
40    pub(crate) prost_types: bool,
41    pub(crate) strip_enum_prefix: bool,
42    pub(crate) out_dir: Option<PathBuf>,
43    pub(crate) extern_paths: Vec<(String, String)>,
44    pub(crate) default_package_filename: String,
45    pub(crate) enable_type_names: bool,
46    pub(crate) type_name_domains: PathMap<String>,
47    pub(crate) protoc_args: Vec<OsString>,
48    pub(crate) protoc_executable: PathBuf,
49    pub(crate) disable_comments: PathMap<()>,
50    pub(crate) skip_debug: PathMap<()>,
51    pub(crate) skip_protoc_run: bool,
52    pub(crate) skip_source_info: bool,
53    pub(crate) include_file: Option<PathBuf>,
54    pub(crate) prost_path: Option<String>,
55    #[cfg(feature = "format")]
56    pub(crate) fmt: bool,
57}
58
59impl Config {
60    /// Creates a new code generator configuration with default options.
61    pub fn new() -> Config {
62        Config::default()
63    }
64
65    /// Configure the code generator to generate Rust [`BTreeMap`][1] fields for Protobuf
66    /// [`map`][2] type fields.
67    ///
68    /// # Arguments
69    ///
70    /// **`paths`** - paths to specific fields, messages, or packages which should use a Rust
71    /// `BTreeMap` for Protobuf `map` fields. Paths are specified in terms of the Protobuf type
72    /// name (not the generated Rust type name). Paths with a leading `.` are treated as fully
73    /// qualified names. Paths without a leading `.` are treated as relative, and are suffix
74    /// matched on the fully qualified field name. If a Protobuf map field matches any of the
75    /// paths, a Rust `BTreeMap` field is generated instead of the default [`HashMap`][3].
76    ///
77    /// The matching is done on the Protobuf names, before converting to Rust-friendly casing
78    /// standards.
79    ///
80    /// # Examples
81    ///
82    /// ```rust
83    /// # let mut config = prost_build::Config::new();
84    /// // Match a specific field in a message type.
85    /// config.btree_map(&[".my_messages.MyMessageType.my_map_field"]);
86    ///
87    /// // Match all map fields in a message type.
88    /// config.btree_map(&[".my_messages.MyMessageType"]);
89    ///
90    /// // Match all map fields in a package.
91    /// config.btree_map(&[".my_messages"]);
92    ///
93    /// // Match all map fields. Specially useful in `no_std` contexts.
94    /// config.btree_map(&["."]);
95    ///
96    /// // Match all map fields in a nested message.
97    /// config.btree_map(&[".my_messages.MyMessageType.MyNestedMessageType"]);
98    ///
99    /// // Match all fields named 'my_map_field'.
100    /// config.btree_map(&["my_map_field"]);
101    ///
102    /// // Match all fields named 'my_map_field' in messages named 'MyMessageType', regardless of
103    /// // package or nesting.
104    /// config.btree_map(&["MyMessageType.my_map_field"]);
105    ///
106    /// // Match all fields named 'my_map_field', and all fields in the 'foo.bar' package.
107    /// config.btree_map(&["my_map_field", ".foo.bar"]);
108    /// ```
109    ///
110    /// [1]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
111    /// [2]: https://developers.google.com/protocol-buffers/docs/proto3#maps
112    /// [3]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
113    pub fn btree_map<I, S>(&mut self, paths: I) -> &mut Self
114    where
115        I: IntoIterator<Item = S>,
116        S: AsRef<str>,
117    {
118        self.map_type.clear();
119        for matcher in paths {
120            self.map_type
121                .insert(matcher.as_ref().to_string(), MapType::BTreeMap);
122        }
123        self
124    }
125
126    /// Configure the code generator to generate Rust [`bytes::Bytes`](prost::bytes::Bytes) fields for Protobuf
127    /// [`bytes`][2] type fields.
128    ///
129    /// # Arguments
130    ///
131    /// **`paths`** - paths to specific fields, messages, or packages which should use a Rust
132    /// `Bytes` for Protobuf `bytes` fields. Paths are specified in terms of the Protobuf type
133    /// name (not the generated Rust type name). Paths with a leading `.` are treated as fully
134    /// qualified names. Paths without a leading `.` are treated as relative, and are suffix
135    /// matched on the fully qualified field name. If a Protobuf map field matches any of the
136    /// paths, a Rust `Bytes` field is generated instead of the default [`Vec<u8>`][3].
137    ///
138    /// The matching is done on the Protobuf names, before converting to Rust-friendly casing
139    /// standards.
140    ///
141    /// # Examples
142    ///
143    /// ```rust
144    /// # let mut config = prost_build::Config::new();
145    /// // Match a specific field in a message type.
146    /// config.bytes(&[".my_messages.MyMessageType.my_bytes_field"]);
147    ///
148    /// // Match all bytes fields in a message type.
149    /// config.bytes(&[".my_messages.MyMessageType"]);
150    ///
151    /// // Match all bytes fields in a package.
152    /// config.bytes(&[".my_messages"]);
153    ///
154    /// // Match all bytes fields. Specially useful in `no_std` contexts.
155    /// config.bytes(&["."]);
156    ///
157    /// // Match all bytes fields in a nested message.
158    /// config.bytes(&[".my_messages.MyMessageType.MyNestedMessageType"]);
159    ///
160    /// // Match all fields named 'my_bytes_field'.
161    /// config.bytes(&["my_bytes_field"]);
162    ///
163    /// // Match all fields named 'my_bytes_field' in messages named 'MyMessageType', regardless of
164    /// // package or nesting.
165    /// config.bytes(&["MyMessageType.my_bytes_field"]);
166    ///
167    /// // Match all fields named 'my_bytes_field', and all fields in the 'foo.bar' package.
168    /// config.bytes(&["my_bytes_field", ".foo.bar"]);
169    /// ```
170    ///
171    /// [2]: https://developers.google.com/protocol-buffers/docs/proto3#scalar
172    /// [3]: https://doc.rust-lang.org/std/vec/struct.Vec.html
173    pub fn bytes<I, S>(&mut self, paths: I) -> &mut Self
174    where
175        I: IntoIterator<Item = S>,
176        S: AsRef<str>,
177    {
178        self.bytes_type.clear();
179        for matcher in paths {
180            self.bytes_type
181                .insert(matcher.as_ref().to_string(), BytesType::Bytes);
182        }
183        self
184    }
185
186    /// Add additional attribute to matched fields.
187    ///
188    /// # Arguments
189    ///
190    /// **`path`** - a path matching any number of fields. These fields get the attribute.
191    /// For details about matching fields see [`btree_map`](Self::btree_map).
192    ///
193    /// **`attribute`** - an arbitrary string that'll be placed before each matched field. The
194    /// expected usage are additional attributes, usually in concert with whole-type
195    /// attributes set with [`type_attribute`](Self::type_attribute), but it is not
196    /// checked and anything can be put there.
197    ///
198    /// Note that the calls to this method are cumulative ‒ if multiple paths from multiple calls
199    /// match the same field, the field gets all the corresponding attributes.
200    ///
201    /// # Examples
202    ///
203    /// ```rust
204    /// # let mut config = prost_build::Config::new();
205    /// // Prost renames fields named `in` to `in_`. But if serialized through serde,
206    /// // they should as `in`.
207    /// config.field_attribute("in", "#[serde(rename = \"in\")]");
208    /// ```
209    pub fn field_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
210    where
211        P: AsRef<str>,
212        A: AsRef<str>,
213    {
214        self.field_attributes
215            .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
216        self
217    }
218
219    /// Add additional attribute to matched messages, enums and one-ofs.
220    ///
221    /// # Arguments
222    ///
223    /// **`paths`** - a path matching any number of types. It works the same way as in
224    /// [`btree_map`](Self::btree_map), just with the field name omitted.
225    ///
226    /// **`attribute`** - an arbitrary string to be placed before each matched type. The
227    /// expected usage are additional attributes, but anything is allowed.
228    ///
229    /// The calls to this method are cumulative. They don't overwrite previous calls and if a
230    /// type is matched by multiple calls of the method, all relevant attributes are added to
231    /// it.
232    ///
233    /// For things like serde it might be needed to combine with [field
234    /// attributes](Self::field_attribute).
235    ///
236    /// # Examples
237    ///
238    /// ```rust
239    /// # let mut config = prost_build::Config::new();
240    /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
241    /// config.type_attribute(".", "#[derive(Eq)]");
242    /// // Some messages want to be serializable with serde as well.
243    /// config.type_attribute("my_messages.MyMessageType",
244    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
245    /// config.type_attribute("my_messages.MyMessageType.MyNestedMessageType",
246    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
247    /// ```
248    ///
249    /// # Oneof fields
250    ///
251    /// The `oneof` fields don't have a type name of their own inside Protobuf. Therefore, the
252    /// field name can be used both with `type_attribute` and `field_attribute` ‒ the first is
253    /// placed before the `enum` type definition, the other before the field inside corresponding
254    /// message `struct`.
255    ///
256    /// In other words, to place an attribute on the `enum` implementing the `oneof`, the match
257    /// would look like `my_messages.MyMessageType.oneofname`.
258    pub fn type_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
259    where
260        P: AsRef<str>,
261        A: AsRef<str>,
262    {
263        self.type_attributes
264            .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
265        self
266    }
267
268    /// Add additional attribute to matched messages.
269    ///
270    /// # Arguments
271    ///
272    /// **`paths`** - a path matching any number of types. It works the same way as in
273    /// [`btree_map`](Self::btree_map), just with the field name omitted.
274    ///
275    /// **`attribute`** - an arbitrary string to be placed before each matched type. The
276    /// expected usage are additional attributes, but anything is allowed.
277    ///
278    /// The calls to this method are cumulative. They don't overwrite previous calls and if a
279    /// type is matched by multiple calls of the method, all relevant attributes are added to
280    /// it.
281    ///
282    /// For things like serde it might be needed to combine with [field
283    /// attributes](Self::field_attribute).
284    ///
285    /// # Examples
286    ///
287    /// ```rust
288    /// # let mut config = prost_build::Config::new();
289    /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
290    /// config.message_attribute(".", "#[derive(Eq)]");
291    /// // Some messages want to be serializable with serde as well.
292    /// config.message_attribute("my_messages.MyMessageType",
293    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
294    /// config.message_attribute("my_messages.MyMessageType.MyNestedMessageType",
295    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
296    /// ```
297    pub fn message_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
298    where
299        P: AsRef<str>,
300        A: AsRef<str>,
301    {
302        self.message_attributes
303            .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
304        self
305    }
306
307    /// Add additional attribute to matched enums and one-ofs.
308    ///
309    /// # Arguments
310    ///
311    /// **`paths`** - a path matching any number of types. It works the same way as in
312    /// [`btree_map`](Self::btree_map), just with the field name omitted.
313    ///
314    /// **`attribute`** - an arbitrary string to be placed before each matched type. The
315    /// expected usage are additional attributes, but anything is allowed.
316    ///
317    /// The calls to this method are cumulative. They don't overwrite previous calls and if a
318    /// type is matched by multiple calls of the method, all relevant attributes are added to
319    /// it.
320    ///
321    /// For things like serde it might be needed to combine with [field
322    /// attributes](Self::field_attribute).
323    ///
324    /// # Examples
325    ///
326    /// ```rust
327    /// # let mut config = prost_build::Config::new();
328    /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
329    /// config.enum_attribute(".", "#[derive(Eq)]");
330    /// // Some messages want to be serializable with serde as well.
331    /// config.enum_attribute("my_messages.MyEnumType",
332    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
333    /// config.enum_attribute("my_messages.MyMessageType.MyNestedEnumType",
334    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
335    /// ```
336    ///
337    /// # Oneof fields
338    ///
339    /// The `oneof` fields don't have a type name of their own inside Protobuf. Therefore, the
340    /// field name can be used both with `enum_attribute` and `field_attribute` ‒ the first is
341    /// placed before the `enum` type definition, the other before the field inside corresponding
342    /// message `struct`.
343    ///
344    /// In other words, to place an attribute on the `enum` implementing the `oneof`, the match
345    /// would look like `my_messages.MyNestedMessageType.oneofname`.
346    pub fn enum_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
347    where
348        P: AsRef<str>,
349        A: AsRef<str>,
350    {
351        self.enum_attributes
352            .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
353        self
354    }
355
356    /// Wrap matched fields in a `Box`.
357    ///
358    /// # Arguments
359    ///
360    /// **`path`** - a path matching any number of fields. These fields get the attribute.
361    /// For details about matching fields see [`btree_map`](Self::btree_map).
362    ///
363    /// # Examples
364    ///
365    /// ```rust
366    /// # let mut config = prost_build::Config::new();
367    /// config.boxed(".my_messages.MyMessageType.my_field");
368    /// ```
369    pub fn boxed<P>(&mut self, path: P) -> &mut Self
370    where
371        P: AsRef<str>,
372    {
373        self.boxed.insert(path.as_ref().to_string(), ());
374        self
375    }
376
377    /// Configures the code generator to use the provided service generator.
378    pub fn service_generator(&mut self, service_generator: Box<dyn ServiceGenerator>) -> &mut Self {
379        self.service_generator = Some(service_generator);
380        self
381    }
382
383    /// Configures the code generator to not use the `prost_types` crate for Protobuf well-known
384    /// types, and instead generate Protobuf well-known types from their `.proto` definitions.
385    pub fn compile_well_known_types(&mut self) -> &mut Self {
386        self.prost_types = false;
387        self
388    }
389
390    /// Configures the code generator to omit documentation comments on generated Protobuf types.
391    ///
392    /// # Example
393    ///
394    /// Occasionally `.proto` files contain code blocks which are not valid Rust. To avoid doctest
395    /// failures, annotate the invalid code blocks with an [`ignore` or `no_run` attribute][1], or
396    /// disable doctests for the crate with a [Cargo.toml entry][2]. If neither of these options
397    /// are possible, then omit comments on generated code during doctest builds:
398    ///
399    /// ```rust,no_run
400    /// # fn main() -> std::io::Result<()> {
401    /// let mut config = prost_build::Config::new();
402    /// config.disable_comments(&["."]);
403    /// config.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
404    /// #     Ok(())
405    /// # }
406    /// ```
407    ///
408    /// As with other options which take a set of paths, comments can be disabled on a per-package
409    /// or per-symbol basis.
410    ///
411    /// [1]: https://doc.rust-lang.org/rustdoc/documentation-tests.html#attributes
412    /// [2]: https://doc.rust-lang.org/cargo/reference/cargo-targets.html#configuring-a-target
413    pub fn disable_comments<I, S>(&mut self, paths: I) -> &mut Self
414    where
415        I: IntoIterator<Item = S>,
416        S: AsRef<str>,
417    {
418        self.disable_comments.clear();
419        for matcher in paths {
420            self.disable_comments
421                .insert(matcher.as_ref().to_string(), ());
422        }
423        self
424    }
425
426    /// Skips generating `impl Debug` for types
427    pub fn skip_debug<I, S>(&mut self, paths: I) -> &mut Self
428    where
429        I: IntoIterator<Item = S>,
430        S: AsRef<str>,
431    {
432        self.skip_debug.clear();
433        for matcher in paths {
434            self.skip_debug.insert(matcher.as_ref().to_string(), ());
435        }
436        self
437    }
438
439    /// Declare an externally provided Protobuf package or type.
440    ///
441    /// `extern_path` allows `prost` types in external crates to be referenced in generated code.
442    ///
443    /// When `prost` compiles a `.proto` which includes an import of another `.proto`, it will
444    /// automatically recursively compile the imported file as well. `extern_path` can be used
445    /// to instead substitute types from an external crate.
446    ///
447    /// # Example
448    ///
449    /// As an example, consider a crate, `uuid`, with a `prost`-generated `Uuid` type:
450    ///
451    /// ```proto
452    /// // uuid.proto
453    ///
454    /// syntax = "proto3";
455    /// package uuid;
456    ///
457    /// message Uuid {
458    ///     string uuid_str = 1;
459    /// }
460    /// ```
461    ///
462    /// The `uuid` crate implements some traits for `Uuid`, and publicly exports it:
463    ///
464    /// ```rust,ignore
465    /// // lib.rs in the uuid crate
466    ///
467    /// include!(concat!(env!("OUT_DIR"), "/uuid.rs"));
468    ///
469    /// pub trait DoSomething {
470    ///     fn do_it(&self);
471    /// }
472    ///
473    /// impl DoSomething for Uuid {
474    ///     fn do_it(&self) {
475    ///         println!("Done");
476    ///     }
477    /// }
478    /// ```
479    ///
480    /// A separate crate, `my_application`, uses `prost` to generate message types which reference
481    /// `Uuid`:
482    ///
483    /// ```proto
484    /// // my_application.proto
485    ///
486    /// syntax = "proto3";
487    /// package my_application;
488    ///
489    /// import "uuid.proto";
490    ///
491    /// message MyMessage {
492    ///     uuid.Uuid message_id = 1;
493    ///     string some_payload = 2;
494    /// }
495    /// ```
496    ///
497    /// Additionally, `my_application` depends on the trait impls provided by the `uuid` crate:
498    ///
499    /// ```rust,ignore
500    /// // `main.rs` of `my_application`
501    ///
502    /// use uuid::{DoSomething, Uuid};
503    ///
504    /// include!(concat!(env!("OUT_DIR"), "/my_application.rs"));
505    ///
506    /// pub fn process_message(msg: MyMessage) {
507    ///     if let Some(uuid) = msg.message_id {
508    ///         uuid.do_it();
509    ///     }
510    /// }
511    /// ```
512    ///
513    /// Without configuring `uuid` as an external path in `my_application`'s `build.rs`, `prost`
514    /// would compile a completely separate version of the `Uuid` type, and `process_message` would
515    /// fail to compile. However, if `my_application` configures `uuid` as an extern path with a
516    /// call to `.extern_path(".uuid", "::uuid")`, `prost` will use the external type instead of
517    /// compiling a new version of `Uuid`. Note that the configuration could also be specified as
518    /// `.extern_path(".uuid.Uuid", "::uuid::Uuid")` if only the `Uuid` type were externally
519    /// provided, and not the whole `uuid` package.
520    ///
521    /// # Usage
522    ///
523    /// `extern_path` takes a fully-qualified Protobuf path, and the corresponding Rust path that
524    /// it will be substituted with in generated code. The Protobuf path can refer to a package or
525    /// a type, and the Rust path should correspondingly refer to a Rust module or type.
526    ///
527    /// ```rust
528    /// # let mut config = prost_build::Config::new();
529    /// // Declare the `uuid` Protobuf package and all nested packages and types as externally
530    /// // provided by the `uuid` crate.
531    /// config.extern_path(".uuid", "::uuid");
532    ///
533    /// // Declare the `foo.bar.baz` Protobuf package and all nested packages and types as
534    /// // externally provided by the `foo_bar_baz` crate.
535    /// config.extern_path(".foo.bar.baz", "::foo_bar_baz");
536    ///
537    /// // Declare the `uuid.Uuid` Protobuf type (and all nested types) as externally provided
538    /// // by the `uuid` crate's `Uuid` type.
539    /// config.extern_path(".uuid.Uuid", "::uuid::Uuid");
540    /// ```
541    pub fn extern_path<P1, P2>(&mut self, proto_path: P1, rust_path: P2) -> &mut Self
542    where
543        P1: Into<String>,
544        P2: Into<String>,
545    {
546        self.extern_paths
547            .push((proto_path.into(), rust_path.into()));
548        self
549    }
550
551    /// When set, the `FileDescriptorSet` generated by `protoc` is written to the provided
552    /// filesystem path.
553    ///
554    /// This option can be used in conjunction with the [`include_bytes!`] macro and the types in
555    /// the `prost-types` crate for implementing reflection capabilities, among other things.
556    ///
557    /// ## Example
558    ///
559    /// In `build.rs`:
560    ///
561    /// ```rust, no_run
562    /// # use std::env;
563    /// # use std::path::PathBuf;
564    /// # let mut config = prost_build::Config::new();
565    /// config.file_descriptor_set_path(
566    ///     PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR environment variable not set"))
567    ///         .join("file_descriptor_set.bin"));
568    /// ```
569    ///
570    /// In `lib.rs`:
571    ///
572    /// ```rust,ignore
573    /// let file_descriptor_set_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
574    /// let file_descriptor_set = prost_types::FileDescriptorSet::decode(&file_descriptor_set_bytes[..]).unwrap();
575    /// ```
576    pub fn file_descriptor_set_path<P>(&mut self, path: P) -> &mut Self
577    where
578        P: Into<PathBuf>,
579    {
580        self.file_descriptor_set_path = Some(path.into());
581        self
582    }
583
584    /// In combination with with `file_descriptor_set_path`, this can be used to provide a file
585    /// descriptor set as an input file, rather than having prost-build generate the file by calling
586    /// protoc.
587    ///
588    /// In `build.rs`:
589    ///
590    /// ```rust
591    /// # let mut config = prost_build::Config::new();
592    /// config.file_descriptor_set_path("path/from/build/system")
593    ///     .skip_protoc_run()
594    ///     .compile_protos(&["src/items.proto"], &["src/"]);
595    /// ```
596    ///
597    pub fn skip_protoc_run(&mut self) -> &mut Self {
598        self.skip_protoc_run = true;
599        self
600    }
601
602    /// Configures the code generator to remove surrounding comments and documentation.
603    ///
604    /// If enabled, this will cause `protoc` to not be passed the `--include_source_info` argument.
605    /// Typically, `--include_source_info` is passed by default, but it results in larger
606    /// [`FileDescriptorSet`s](https://github.com/protocolbuffers/protobuf/blob/cff254d32f850ba8186227ce6775b3f01a1f8cf8/src/google/protobuf/descriptor.proto#L54-L66) that include information about the
607    /// original location of each declaration in the source file as well as surrounding
608    /// comments and documentation.
609    ///
610    /// In `build.rs`:
611    ///
612    /// ```rust
613    /// # let mut config = prost_build::Config::new();
614    /// config.file_descriptor_set_path("path/from/build/system")
615    ///     .skip_source_info()
616    ///     .compile_protos(&["src/items.proto"], &["src/"]);
617    /// ```
618    pub fn skip_source_info(&mut self) -> &mut Self {
619        self.skip_source_info = true;
620        self
621    }
622
623    /// Configures the code generator to not strip the enum name from variant names.
624    ///
625    /// Protobuf enum definitions commonly include the enum name as a prefix of every variant name.
626    /// This style is non-idiomatic in Rust, so by default `prost` strips the enum name prefix from
627    /// variants which include it. Configuring this option prevents `prost` from stripping the
628    /// prefix.
629    pub fn retain_enum_prefix(&mut self) -> &mut Self {
630        self.strip_enum_prefix = false;
631        self
632    }
633
634    /// Configures the output directory where generated Rust files will be written.
635    ///
636    /// If unset, defaults to the `OUT_DIR` environment variable. `OUT_DIR` is set by Cargo when
637    /// executing build scripts, so `out_dir` typically does not need to be configured.
638    pub fn out_dir<P>(&mut self, path: P) -> &mut Self
639    where
640        P: Into<PathBuf>,
641    {
642        self.out_dir = Some(path.into());
643        self
644    }
645
646    /// Configures what filename protobufs with no package definition are written to.
647    /// The filename will be appended with the `.rs` extension.
648    pub fn default_package_filename<S>(&mut self, filename: S) -> &mut Self
649    where
650        S: Into<String>,
651    {
652        self.default_package_filename = filename.into();
653        self
654    }
655
656    /// Configures the code generator to include type names.
657    ///
658    /// Message types will implement `Name` trait, which provides type and package name.
659    /// This is needed for encoding messages as `Any` type.
660    pub fn enable_type_names(&mut self) -> &mut Self {
661        self.enable_type_names = true;
662        self
663    }
664
665    /// Specify domain names to use with message type URLs.
666    ///
667    /// # Domains
668    ///
669    /// **`paths`** - a path matching any number of types. It works the same way as in
670    /// [`btree_map`](Self::btree_map), just with the field name omitted.
671    ///
672    /// **`domain`** - an arbitrary string to be used as a prefix for type URLs.
673    ///
674    /// # Examples
675    ///
676    /// ```rust
677    /// # let mut config = prost_build::Config::new();
678    /// // Full type URL of the message `google.profile.Person`,
679    /// // will be `type.googleapis.com/google.profile.Person`.
680    /// config.type_name_domain(&["."], "type.googleapis.com");
681    /// ```
682    pub fn type_name_domain<I, S, D>(&mut self, paths: I, domain: D) -> &mut Self
683    where
684        I: IntoIterator<Item = S>,
685        S: AsRef<str>,
686        D: AsRef<str>,
687    {
688        self.type_name_domains.clear();
689        for matcher in paths {
690            self.type_name_domains
691                .insert(matcher.as_ref().to_string(), domain.as_ref().to_string());
692        }
693        self
694    }
695
696    /// Configures the path that's used for deriving `Message` for generated messages.
697    /// This is mainly useful for generating crates that wish to re-export prost.
698    /// Defaults to `::prost::Message` if not specified.
699    pub fn prost_path<S>(&mut self, path: S) -> &mut Self
700    where
701        S: Into<String>,
702    {
703        self.prost_path = Some(path.into());
704        self
705    }
706
707    /// Add an argument to the `protoc` protobuf compilation invocation.
708    ///
709    /// # Example `build.rs`
710    ///
711    /// ```rust,no_run
712    /// # use std::io::Result;
713    /// fn main() -> Result<()> {
714    ///   let mut prost_build = prost_build::Config::new();
715    ///   // Enable a protoc experimental feature.
716    ///   prost_build.protoc_arg("--experimental_allow_proto3_optional");
717    ///   prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
718    ///   Ok(())
719    /// }
720    /// ```
721    pub fn protoc_arg<S>(&mut self, arg: S) -> &mut Self
722    where
723        S: AsRef<OsStr>,
724    {
725        self.protoc_args.push(arg.as_ref().to_owned());
726        self
727    }
728
729    /// Set the path to `protoc` executable to be used by `prost-build`
730    ///
731    /// Use the provided path to find `protoc`. This can either be a file name which is
732    /// searched for in the `PATH` or an aboslute path to use a specific executable.
733    ///
734    /// # Example `build.rs`
735    ///
736    /// ```rust,no_run
737    /// # use std::io::Result;
738    /// fn main() -> Result<()> {
739    ///   let mut prost_build = prost_build::Config::new();
740    ///   prost_build.protoc_executable("protoc-27.1");
741    ///   prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
742    ///   Ok(())
743    /// }
744    /// ```
745    pub fn protoc_executable<S>(&mut self, executable: S) -> &mut Self
746    where
747        S: Into<PathBuf>,
748    {
749        self.protoc_executable = executable.into();
750        self
751    }
752
753    /// Configures the optional module filename for easy inclusion of all generated Rust files
754    ///
755    /// If set, generates a file (inside the `OUT_DIR` or `out_dir()` as appropriate) which contains
756    /// a set of `pub mod XXX` statements combining to load all Rust files generated.  This can allow
757    /// for a shortcut where multiple related proto files have been compiled together resulting in
758    /// a semi-complex set of includes.
759    ///
760    /// Turning a need for:
761    ///
762    /// ```rust,no_run,ignore
763    /// pub mod Foo {
764    ///     pub mod Bar {
765    ///         include!(concat!(env!("OUT_DIR"), "/foo.bar.rs"));
766    ///     }
767    ///     pub mod Baz {
768    ///         include!(concat!(env!("OUT_DIR"), "/foo.baz.rs"));
769    ///     }
770    /// }
771    /// ```
772    ///
773    /// Into the simpler:
774    ///
775    /// ```rust,no_run,ignore
776    /// include!(concat!(env!("OUT_DIR"), "/_includes.rs"));
777    /// ```
778    pub fn include_file<P>(&mut self, path: P) -> &mut Self
779    where
780        P: Into<PathBuf>,
781    {
782        self.include_file = Some(path.into());
783        self
784    }
785
786    // IMPROVEMENT: https://github.com/tokio-rs/prost/pull/1022/files#r1563818651
787    /// Configures the code generator to format the output code via `prettyplease`.
788    ///
789    /// By default, this is enabled but if the `format` feature is not enabled this does
790    /// nothing.
791    #[cfg(feature = "format")]
792    pub fn format(&mut self, enabled: bool) -> &mut Self {
793        self.fmt = enabled;
794        self
795    }
796
797    /// Compile a [`FileDescriptorSet`] into Rust files during a Cargo build with
798    /// additional code generator configuration options.
799    ///
800    /// This method is like `compile_protos` function except it does not invoke `protoc`
801    /// and instead requires the user to supply a [`FileDescriptorSet`].
802    ///
803    /// # Example `build.rs`
804    ///
805    /// ```rust,no_run
806    /// # use prost_types::FileDescriptorSet;
807    /// # fn fds() -> FileDescriptorSet { todo!() }
808    /// fn main() -> std::io::Result<()> {
809    ///   let file_descriptor_set = fds();
810    ///
811    ///   prost_build::Config::new()
812    ///     .compile_fds(file_descriptor_set)
813    /// }
814    /// ```
815    pub fn compile_fds(&mut self, fds: FileDescriptorSet) -> Result<()> {
816        let mut target_is_env = false;
817        let target: PathBuf = self.out_dir.clone().map(Ok).unwrap_or_else(|| {
818            env::var_os("OUT_DIR")
819                .ok_or_else(|| {
820                    Error::new(ErrorKind::Other, "OUT_DIR environment variable is not set")
821                })
822                .map(|val| {
823                    target_is_env = true;
824                    Into::into(val)
825                })
826        })?;
827
828        let requests = fds
829            .file
830            .into_iter()
831            .map(|descriptor| {
832                (
833                    Module::from_protobuf_package_name(descriptor.package()),
834                    descriptor,
835                )
836            })
837            .collect::<Vec<_>>();
838
839        let file_names = requests
840            .iter()
841            .map(|req| {
842                (
843                    req.0.clone(),
844                    req.0.to_file_name_or(&self.default_package_filename),
845                )
846            })
847            .collect::<HashMap<Module, String>>();
848
849        let modules = self.generate(requests)?;
850        for (module, content) in &modules {
851            let file_name = file_names
852                .get(module)
853                .expect("every module should have a filename");
854            let output_path = target.join(file_name);
855
856            write_file_if_changed(&output_path, content.as_bytes())?;
857        }
858
859        if let Some(ref include_file) = self.include_file {
860            let path = target.join(include_file);
861            trace!("Writing include file: {}", path.display());
862            let mut buffer = Vec::new();
863            self.write_line(&mut buffer, 0, "// This file is @generated by prost-build.")?;
864            self.write_includes(
865                modules.keys().collect(),
866                &mut buffer,
867                if target_is_env { None } else { Some(&target) },
868                &file_names,
869            )?;
870
871            write_file_if_changed(&path, &buffer)?;
872        }
873
874        Ok(())
875    }
876
877    /// Loads `.proto` files as a [`FileDescriptorSet`]. This allows inspection of the descriptors
878    /// before calling [`Config::compile_fds`]. This could be used to change [`Config`]
879    /// attributes after introspecting what is actually present in the `.proto` files.
880    ///
881    /// # Example `build.rs`
882    ///
883    /// ```rust,no_run
884    /// # use prost_types::FileDescriptorSet;
885    /// # use prost_build::Config;
886    /// fn main() -> std::io::Result<()> {
887    ///   let mut config = Config::new();
888    ///   let file_descriptor_set = config.load_fds(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
889    ///
890    ///   // Add custom attributes to messages that are service inputs or outputs.
891    ///   for file in &file_descriptor_set.file {
892    ///       for service in &file.service {
893    ///           for method in &service.method {
894    ///               if let Some(input) = &method.input_type {
895    ///                   config.message_attribute(input, "#[derive(custom_proto::Input)]");
896    ///               }
897    ///               if let Some(output) = &method.output_type {
898    ///                   config.message_attribute(output, "#[derive(custom_proto::Output)]");
899    ///               }
900    ///           }
901    ///       }
902    ///   }
903    ///
904    ///   config.compile_fds(file_descriptor_set)
905    /// }
906    /// ```
907    pub fn load_fds(
908        &mut self,
909        protos: &[impl AsRef<Path>],
910        includes: &[impl AsRef<Path>],
911    ) -> Result<FileDescriptorSet> {
912        let tmp;
913        let file_descriptor_set_path = if let Some(path) = &self.file_descriptor_set_path {
914            path.clone()
915        } else {
916            if self.skip_protoc_run {
917                return Err(Error::new(
918                    ErrorKind::Other,
919                    "file_descriptor_set_path is required with skip_protoc_run",
920                ));
921            }
922            tmp = tempfile::Builder::new().prefix("prost-build").tempdir()?;
923            tmp.path().join("prost-descriptor-set")
924        };
925
926        if !self.skip_protoc_run {
927            let mut cmd = Command::new(&self.protoc_executable);
928            cmd.arg("--include_imports");
929            if !self.skip_source_info {
930                cmd.arg("--include_source_info");
931            }
932            cmd.arg("-o").arg(&file_descriptor_set_path);
933
934            for include in includes {
935                if include.as_ref().exists() {
936                    cmd.arg("-I").arg(include.as_ref());
937                } else {
938                    debug!(
939                        "ignoring {} since it does not exist.",
940                        include.as_ref().display()
941                    )
942                }
943            }
944
945            // Set the protoc include after the user includes in case the user wants to
946            // override one of the built-in .protos.
947            if let Some(protoc_include) = protoc_include_from_env() {
948                cmd.arg("-I").arg(protoc_include);
949            }
950
951            for arg in &self.protoc_args {
952                cmd.arg(arg);
953            }
954
955            for proto in protos {
956                cmd.arg(proto.as_ref());
957            }
958
959            debug!("Running: {:?}", cmd);
960
961            let output = match cmd.output() {
962            Err(err) if ErrorKind::NotFound == err.kind() => return Err(Error::new(
963                err.kind(),
964                error_message_protoc_not_found()
965            )),
966            Err(err) => return Err(Error::new(
967                err.kind(),
968                format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): (path: {}): {}", &self.protoc_executable.display(), err),
969            )),
970            Ok(output) => output,
971        };
972
973            if !output.status.success() {
974                return Err(Error::new(
975                    ErrorKind::Other,
976                    format!("protoc failed: {}", String::from_utf8_lossy(&output.stderr)),
977                ));
978            }
979        }
980
981        let buf = fs::read(&file_descriptor_set_path).map_err(|e| {
982            Error::new(
983                e.kind(),
984                format!(
985                    "unable to open file_descriptor_set_path: {}, OS: {}",
986                    file_descriptor_set_path.display(),
987                    e
988                ),
989            )
990        })?;
991        let file_descriptor_set = FileDescriptorSet::decode(buf.as_slice()).map_err(|error| {
992            Error::new(
993                ErrorKind::InvalidInput,
994                format!("invalid FileDescriptorSet: {}", error),
995            )
996        })?;
997
998        Ok(file_descriptor_set)
999    }
1000
1001    /// Compile `.proto` files into Rust files during a Cargo build with additional code generator
1002    /// configuration options.
1003    ///
1004    /// This method is like the `prost_build::compile_protos` function, with the added ability to
1005    /// specify non-default code generation options. See that function for more information about
1006    /// the arguments and generated outputs.
1007    ///
1008    /// The `protos` and `includes` arguments are ignored if `skip_protoc_run` is specified.
1009    ///
1010    /// # Example `build.rs`
1011    ///
1012    /// ```rust,no_run
1013    /// # use std::io::Result;
1014    /// fn main() -> Result<()> {
1015    ///   let mut prost_build = prost_build::Config::new();
1016    ///   prost_build.btree_map(&["."]);
1017    ///   prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
1018    ///   Ok(())
1019    /// }
1020    /// ```
1021    pub fn compile_protos(
1022        &mut self,
1023        protos: &[impl AsRef<Path>],
1024        includes: &[impl AsRef<Path>],
1025    ) -> Result<()> {
1026        // TODO: This should probably emit 'rerun-if-changed=PATH' directives for cargo, however
1027        // according to [1] if any are output then those paths replace the default crate root,
1028        // which is undesirable. Figure out how to do it in an additive way; perhaps gcc-rs has
1029        // this figured out.
1030        // [1]: http://doc.crates.io/build-script.html#outputs-of-the-build-script
1031
1032        let file_descriptor_set = self.load_fds(protos, includes)?;
1033
1034        self.compile_fds(file_descriptor_set)
1035    }
1036
1037    pub(crate) fn write_includes(
1038        &self,
1039        mut modules: Vec<&Module>,
1040        outfile: &mut impl Write,
1041        basepath: Option<&PathBuf>,
1042        file_names: &HashMap<Module, String>,
1043    ) -> Result<()> {
1044        modules.sort();
1045
1046        let mut stack = Vec::new();
1047
1048        for module in modules {
1049            while !module.starts_with(&stack) {
1050                stack.pop();
1051                self.write_line(outfile, stack.len(), "}")?;
1052            }
1053            while stack.len() < module.len() {
1054                self.write_line(
1055                    outfile,
1056                    stack.len(),
1057                    &format!("pub mod {} {{", module.part(stack.len())),
1058                )?;
1059                stack.push(module.part(stack.len()).to_owned());
1060            }
1061
1062            let file_name = file_names
1063                .get(module)
1064                .expect("every module should have a filename");
1065
1066            if basepath.is_some() {
1067                self.write_line(
1068                    outfile,
1069                    stack.len(),
1070                    &format!("include!(\"{}\");", file_name),
1071                )?;
1072            } else {
1073                self.write_line(
1074                    outfile,
1075                    stack.len(),
1076                    &format!("include!(concat!(env!(\"OUT_DIR\"), \"/{}\"));", file_name),
1077                )?;
1078            }
1079        }
1080
1081        for depth in (0..stack.len()).rev() {
1082            self.write_line(outfile, depth, "}")?;
1083        }
1084
1085        Ok(())
1086    }
1087
1088    fn write_line(&self, outfile: &mut impl Write, depth: usize, line: &str) -> Result<()> {
1089        outfile.write_all(format!("{}{}\n", ("    ").to_owned().repeat(depth), line).as_bytes())
1090    }
1091
1092    /// Processes a set of modules and file descriptors, returning a map of modules to generated
1093    /// code contents.
1094    ///
1095    /// This is generally used when control over the output should not be managed by Prost,
1096    /// such as in a flow for a `protoc` code generating plugin. When compiling as part of a
1097    /// `build.rs` file, instead use [`Self::compile_protos()`].
1098    pub fn generate(
1099        &mut self,
1100        requests: Vec<(Module, FileDescriptorProto)>,
1101    ) -> Result<HashMap<Module, String>> {
1102        let mut modules = HashMap::new();
1103        let mut packages = HashMap::new();
1104
1105        let message_graph = MessageGraph::new(requests.iter().map(|x| &x.1));
1106        let extern_paths = ExternPaths::new(&self.extern_paths, self.prost_types)
1107            .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?;
1108        let mut context = Context::new(self, message_graph, extern_paths);
1109
1110        for (request_module, request_fd) in requests {
1111            // Only record packages that have services
1112            if !request_fd.service.is_empty() {
1113                packages.insert(request_module.clone(), request_fd.package().to_string());
1114            }
1115            let buf = modules
1116                .entry(request_module.clone())
1117                .or_insert_with(String::new);
1118            CodeGenerator::generate(&mut context, request_fd, buf);
1119            if buf.is_empty() {
1120                // Did not generate any code, remove from list to avoid inclusion in include file or output file list
1121                modules.remove(&request_module);
1122            }
1123        }
1124
1125        if let Some(service_generator) = context.service_generator_mut() {
1126            for (module, package) in packages {
1127                let buf = modules.get_mut(&module).unwrap();
1128                service_generator.finalize_package(&package, buf);
1129            }
1130        }
1131
1132        #[cfg(feature = "format")]
1133        if self.fmt {
1134            for buf in modules.values_mut() {
1135                let file = syn::parse_file(buf).unwrap();
1136                let formatted = prettyplease::unparse(&file);
1137                *buf = formatted;
1138            }
1139        }
1140
1141        self.add_generated_modules(&mut modules);
1142
1143        Ok(modules)
1144    }
1145
1146    fn add_generated_modules(&mut self, modules: &mut HashMap<Module, String>) {
1147        for buf in modules.values_mut() {
1148            let with_generated = "// This file is @generated by prost-build.\n".to_string() + buf;
1149            *buf = with_generated;
1150        }
1151    }
1152}
1153
1154/// Write a slice as the entire contents of a file.
1155///
1156/// This function will create a file if it does not exist,
1157/// and will entirely replace its contents if it does. When
1158/// the contents is already correct, it doesn't touch to the file.
1159fn write_file_if_changed(path: &Path, content: &[u8]) -> std::io::Result<()> {
1160    let previous_content = fs::read(path);
1161
1162    if previous_content
1163        .map(|previous_content| previous_content == content)
1164        .unwrap_or(false)
1165    {
1166        trace!("unchanged: {}", path.display());
1167        Ok(())
1168    } else {
1169        trace!("writing: {}", path.display());
1170        fs::write(path, content)
1171    }
1172}
1173
1174impl default::Default for Config {
1175    fn default() -> Config {
1176        Config {
1177            file_descriptor_set_path: None,
1178            service_generator: None,
1179            map_type: PathMap::default(),
1180            bytes_type: PathMap::default(),
1181            type_attributes: PathMap::default(),
1182            message_attributes: PathMap::default(),
1183            enum_attributes: PathMap::default(),
1184            field_attributes: PathMap::default(),
1185            boxed: PathMap::default(),
1186            prost_types: true,
1187            strip_enum_prefix: true,
1188            out_dir: None,
1189            extern_paths: Vec::new(),
1190            default_package_filename: "_".to_string(),
1191            enable_type_names: false,
1192            type_name_domains: PathMap::default(),
1193            protoc_args: Vec::new(),
1194            protoc_executable: protoc_from_env(),
1195            disable_comments: PathMap::default(),
1196            skip_debug: PathMap::default(),
1197            skip_protoc_run: false,
1198            skip_source_info: false,
1199            include_file: None,
1200            prost_path: None,
1201            #[cfg(feature = "format")]
1202            fmt: true,
1203        }
1204    }
1205}
1206
1207impl fmt::Debug for Config {
1208    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1209        fmt.debug_struct("Config")
1210            .field("file_descriptor_set_path", &self.file_descriptor_set_path)
1211            .field("service_generator", &self.service_generator.is_some())
1212            .field("map_type", &self.map_type)
1213            .field("bytes_type", &self.bytes_type)
1214            .field("type_attributes", &self.type_attributes)
1215            .field("field_attributes", &self.field_attributes)
1216            .field("prost_types", &self.prost_types)
1217            .field("strip_enum_prefix", &self.strip_enum_prefix)
1218            .field("out_dir", &self.out_dir)
1219            .field("extern_paths", &self.extern_paths)
1220            .field("default_package_filename", &self.default_package_filename)
1221            .field("enable_type_names", &self.enable_type_names)
1222            .field("type_name_domains", &self.type_name_domains)
1223            .field("protoc_args", &self.protoc_args)
1224            .field("disable_comments", &self.disable_comments)
1225            .field("skip_debug", &self.skip_debug)
1226            .field("prost_path", &self.prost_path)
1227            .finish()
1228    }
1229}
1230
1231pub fn error_message_protoc_not_found() -> String {
1232    let error_msg = "Could not find `protoc`. If `protoc` is installed, try setting the `PROTOC` environment variable to the path of the `protoc` binary.";
1233
1234    let os_specific_hint = if cfg!(target_os = "macos") {
1235        "To install it on macOS, run `brew install protobuf`."
1236    } else if cfg!(target_os = "linux") {
1237        "To install it on Debian, run `apt-get install protobuf-compiler`."
1238    } else {
1239        "Try installing `protobuf-compiler` or `protobuf` using your package manager."
1240    };
1241    let download_msg =
1242        "It is also available at https://github.com/protocolbuffers/protobuf/releases";
1243
1244    format!(
1245        "{} {} {}  For more information: https://docs.rs/prost-build/#sourcing-protoc",
1246        error_msg, os_specific_hint, download_msg
1247    )
1248}
1249
1250/// Returns the path to the `protoc` binary.
1251pub fn protoc_from_env() -> PathBuf {
1252    env::var_os("PROTOC")
1253        .map(PathBuf::from)
1254        .unwrap_or(PathBuf::from("protoc"))
1255}
1256
1257/// Returns the path to the Protobuf include directory.
1258pub fn protoc_include_from_env() -> Option<PathBuf> {
1259    let protoc_include: PathBuf = env::var_os("PROTOC_INCLUDE")?.into();
1260
1261    if !protoc_include.exists() {
1262        panic!(
1263            "PROTOC_INCLUDE environment variable points to non-existent directory ({})",
1264            protoc_include.display()
1265        );
1266    }
1267    if !protoc_include.is_dir() {
1268        panic!(
1269            "PROTOC_INCLUDE environment variable points to a non-directory file ({})",
1270            protoc_include.display()
1271        );
1272    }
1273
1274    Some(protoc_include)
1275}
1276
1277#[cfg(test)]
1278mod tests {
1279    use super::*;
1280
1281    macro_rules! assert_starts_with {
1282        ($left:expr, $right:expr) => {
1283            match (&$left, &$right) {
1284                (left_val, right_val) => {
1285                    if !(left_val.starts_with(right_val)) {
1286                        panic!(
1287                            "assertion 'starts_with` failed:\nleft: {}\nright: {}",
1288                            left_val, right_val
1289                        )
1290                    }
1291                }
1292            }
1293        };
1294    }
1295
1296    #[test]
1297    fn test_error_protoc_not_found() {
1298        let mut config = Config::new();
1299        config.protoc_executable("path-does-not-exist");
1300
1301        let err = config.load_fds(&[""], &[""]).unwrap_err();
1302        assert_eq!(err.to_string(), error_message_protoc_not_found())
1303    }
1304
1305    #[test]
1306    fn test_error_protoc_not_executable() {
1307        let mut config = Config::new();
1308        config.protoc_executable("src/lib.rs");
1309
1310        let err = config.load_fds(&[""], &[""]).unwrap_err();
1311        assert_starts_with!(err.to_string(), "failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): (path: src/lib.rs): ")
1312    }
1313
1314    #[test]
1315    fn test_error_incorrect_skip_protoc_run() {
1316        let mut config = Config::new();
1317        config.skip_protoc_run();
1318
1319        let err = config.load_fds(&[""], &[""]).unwrap_err();
1320        assert_eq!(
1321            err.to_string(),
1322            "file_descriptor_set_path is required with skip_protoc_run"
1323        )
1324    }
1325
1326    #[test]
1327    fn test_error_protoc_failed() {
1328        let mut config = Config::new();
1329
1330        let err = config.load_fds(&[""], &[""]).unwrap_err();
1331        assert_starts_with!(
1332            err.to_string(),
1333            "protoc failed: You seem to have passed an empty string as one of the arguments to "
1334        )
1335    }
1336
1337    #[test]
1338    fn test_error_non_existing_file_descriptor_set() {
1339        let mut config = Config::new();
1340        config.skip_protoc_run();
1341        config.file_descriptor_set_path("path-does-not-exist");
1342
1343        let err = config.load_fds(&[""], &[""]).unwrap_err();
1344        assert_starts_with!(
1345            err.to_string(),
1346            "unable to open file_descriptor_set_path: path-does-not-exist, OS: "
1347        )
1348    }
1349
1350    #[test]
1351    fn test_error_text_incorrect_file_descriptor_set() {
1352        let mut config = Config::new();
1353        config.skip_protoc_run();
1354        config.file_descriptor_set_path("src/lib.rs");
1355
1356        let err = config.load_fds(&[""], &[""]).unwrap_err();
1357        assert_eq!(
1358            err.to_string(),
1359            "invalid FileDescriptorSet: failed to decode Protobuf message: unexpected end group tag"
1360        )
1361    }
1362
1363    #[test]
1364    fn test_error_unset_out_dir() {
1365        let mut config = Config::new();
1366
1367        let err = config
1368            .compile_fds(FileDescriptorSet::default())
1369            .unwrap_err();
1370        assert_eq!(err.to_string(), "OUT_DIR environment variable is not set")
1371    }
1372}