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}