prost_msg_build/
lib.rs

1#![doc(html_root_url = "https://docs.rs/prost-build/0.11.8")]
2#![allow(clippy::option_as_ref_deref, clippy::format_push_string)]
3
4//! `prost-build` compiles `.proto` files into Rust.
5//!
6//! `prost-build` is designed to be used for build-time code generation as part of a Cargo
7//! build-script.
8//!
9//! ## Example
10//!
11//! Let's create a small crate, `snazzy`, that defines a collection of
12//! snazzy new items in a protobuf file.
13//!
14//! ```bash
15//! $ cargo new snazzy && cd snazzy
16//! ```
17//!
18//! First, add `prost-build`, `prost` and its public dependencies to `Cargo.toml`
19//! (see [crates.io](https://crates.io/crates/prost) for the current versions):
20//!
21//! ```toml
22//! [dependencies]
23//! bytes = <bytes-version>
24//! prost = <prost-version>
25//!
26//! [build-dependencies]
27//! prost-build = { version = <prost-version> }
28//! ```
29//!
30//! Next, add `src/items.proto` to the project:
31//!
32//! ```proto
33//! syntax = "proto3";
34//!
35//! package snazzy.items;
36//!
37//! // A snazzy new shirt!
38//! message Shirt {
39//!     enum Size {
40//!         SMALL = 0;
41//!         MEDIUM = 1;
42//!         LARGE = 2;
43//!     }
44//!
45//!     string color = 1;
46//!     Size size = 2;
47//! }
48//! ```
49//!
50//! To generate Rust code from `items.proto`, we use `prost-build` in the crate's
51//! `build.rs` build-script:
52//!
53//! ```rust,no_run
54//! use std::io::Result;
55//! fn main() -> Result<()> {
56//!     prost_build::compile_protos(&["src/items.proto"], &["src/"])?;
57//!     Ok(())
58//! }
59//! ```
60//!
61//! And finally, in `lib.rs`, include the generated code:
62//!
63//! ```rust,ignore
64//! // Include the `items` module, which is generated from items.proto.
65//! // It is important to maintain the same structure as in the proto.
66//! pub mod snazzy {
67//!     pub mod items {
68//!         include!(concat!(env!("OUT_DIR"), "/snazzy.items.rs"));
69//!     }
70//! }
71//!
72//! use snazzy::items;
73//!
74//! pub fn create_large_shirt(color: String) -> items::Shirt {
75//!     let mut shirt = items::Shirt::default();
76//!     shirt.color = color;
77//!     shirt.set_size(items::shirt::Size::Large);
78//!     shirt
79//! }
80//! ```
81//!
82//! That's it! Run `cargo doc` to see documentation for the generated code. The full
83//! example project can be found on [GitHub](https://github.com/danburkert/snazzy).
84//!
85//! ### Cleaning up Markdown in code docs
86//!
87//! If you are using protobuf files from third parties, where the author of the protobuf
88//! is not treating comments as Markdown, or is, but has codeblocks in their docs,
89//! then you may need to clean up the documentation in order that `cargo test --doc`
90//! will not fail spuriously, and that `cargo doc` doesn't attempt to render the
91//! codeblocks as Rust code.
92//!
93//! To do this, in your `Cargo.toml`, add `features = ["cleanup-markdown"]` to the inclusion
94//! of the `prost-build` crate and when your code is generated, the code docs will automatically
95//! be cleaned up a bit.
96//!
97//! ## Sourcing `protoc`
98//!
99//! `prost-build` depends on the Protocol Buffers compiler, `protoc`, to parse `.proto` files into
100//! a representation that can be transformed into Rust. If set, `prost-build` uses the `PROTOC`
101//! for locating `protoc`. For example, on a macOS system where Protobuf is installed
102//! with Homebrew, set the environment variables to:
103//!
104//! ```bash
105//! PROTOC=/usr/local/bin/protoc
106//! ```
107//!
108//! and in a typical Linux installation:
109//!
110//! ```bash
111//! PROTOC=/usr/bin/protoc
112//! ```
113//!
114//! If no `PROTOC` environment variable is set then `prost-build` will search the
115//! current path for `protoc` or `protoc.exe`. If `prost-build` can not find `protoc`
116//! via these methods the `compile_protos` method will fail.
117//!
118//! ### Compiling `protoc` from source
119//!
120//! To compile `protoc` from source you can use the `protobuf-src` crate and
121//! set the correct environment variables.
122//! ```no_run,ignore, rust
123//! std::env::set_var("PROTOC", protobuf_src::protoc());
124//!
125//! // Now compile your proto files via prost-build
126//! ```
127//!
128//! [`protobuf-src`]: https://docs.rs/protobuf-src
129
130use std::collections::HashMap;
131use std::default;
132use std::env;
133use std::ffi::{OsStr, OsString};
134use std::fmt;
135use std::fs;
136use std::io::{Error, ErrorKind, Result, Write};
137use std::ops::RangeToInclusive;
138use std::path::{Path, PathBuf};
139use std::process::Command;
140
141use log::debug;
142use log::trace;
143
144use prost::Message;
145use prost_types::{FileDescriptorProto, FileDescriptorSet};
146
147pub use crate::ast::{Comments, Method, Service};
148use crate::code_generator::CodeGenerator;
149use crate::extern_paths::ExternPaths;
150use crate::ident::to_snake;
151use crate::message_graph::MessageGraph;
152use crate::path::PathMap;
153
154mod ast;
155mod code_generator;
156mod extern_paths;
157mod ident;
158mod message_graph;
159mod path;
160
161/// A service generator takes a service descriptor and generates Rust code.
162///
163/// `ServiceGenerator` can be used to generate application-specific interfaces
164/// or implementations for Protobuf service definitions.
165///
166/// Service generators are registered with a code generator using the
167/// `Config::service_generator` method.
168///
169/// A viable scenario is that an RPC framework provides a service generator. It generates a trait
170/// describing methods of the service and some glue code to call the methods of the trait, defining
171/// details like how errors are handled or if it is asynchronous. Then the user provides an
172/// implementation of the generated trait in the application code and plugs it into the framework.
173///
174/// Such framework isn't part of Prost at present.
175pub trait ServiceGenerator {
176    /// Generates a Rust interface or implementation for a service, writing the
177    /// result to `buf`.
178    fn generate(&mut self, service: Service, buf: &mut String);
179
180    /// Finalizes the generation process.
181    ///
182    /// In case there's something that needs to be output at the end of the generation process, it
183    /// goes here. Similar to [`generate`](#method.generate), the output should be appended to
184    /// `buf`.
185    ///
186    /// An example can be a module or other thing that needs to appear just once, not for each
187    /// service generated.
188    ///
189    /// This still can be called multiple times in a lifetime of the service generator, because it
190    /// is called once per `.proto` file.
191    ///
192    /// The default implementation is empty and does nothing.
193    fn finalize(&mut self, _buf: &mut String) {}
194
195    /// Finalizes the generation process for an entire protobuf package.
196    ///
197    /// This differs from [`finalize`](#method.finalize) by where (and how often) it is called
198    /// during the service generator life cycle. This method is called once per protobuf package,
199    /// making it ideal for grouping services within a single package spread across multiple
200    /// `.proto` files.
201    ///
202    /// The default implementation is empty and does nothing.
203    fn finalize_package(&mut self, _package: &str, _buf: &mut String) {}
204}
205
206/// The map collection type to output for Protobuf `map` fields.
207#[non_exhaustive]
208#[derive(Clone, Copy, Debug, PartialEq, Default)]
209enum MapType {
210    /// The [`std::collections::HashMap`] type.
211    #[default]
212    HashMap,
213    /// The [`std::collections::BTreeMap`] type.
214    BTreeMap,
215}
216
217/// The bytes collection type to output for Protobuf `bytes` fields.
218#[non_exhaustive]
219#[derive(Clone, Copy, Debug, PartialEq, Default)]
220enum BytesType {
221    /// The [`alloc::collections::Vec::<u8>`] type.
222    #[default]
223    Vec,
224    /// The [`bytes::Bytes`] type.
225    Bytes,
226}
227
228/// Configuration options for Protobuf code generation.
229///
230/// This configuration builder can be used to set non-default code generation options.
231pub struct Config {
232    file_descriptor_set_path: Option<PathBuf>,
233    service_generator: Option<Box<dyn ServiceGenerator>>,
234    map_type: PathMap<MapType>,
235    bytes_type: PathMap<BytesType>,
236    type_attributes: PathMap<String>,
237    message_attributes: PathMap<String>,
238    enum_attributes: PathMap<String>,
239    field_attributes: PathMap<String>,
240    prost_types: bool,
241    strip_enum_prefix: bool,
242    out_dir: Option<PathBuf>,
243    extern_paths: Vec<(String, String)>,
244    default_package_filename: String,
245    protoc_args: Vec<OsString>,
246    disable_comments: PathMap<()>,
247    skip_protoc_run: bool,
248    include_file: Option<PathBuf>,
249    prost_path: Option<String>,
250    fmt: bool,
251}
252
253impl Config {
254    /// Creates a new code generator configuration with default options.
255    pub fn new() -> Config {
256        Config::default()
257    }
258
259    /// Configure the code generator to generate Rust [`BTreeMap`][1] fields for Protobuf
260    /// [`map`][2] type fields.
261    ///
262    /// # Arguments
263    ///
264    /// **`paths`** - paths to specific fields, messages, or packages which should use a Rust
265    /// `BTreeMap` for Protobuf `map` fields. Paths are specified in terms of the Protobuf type
266    /// name (not the generated Rust type name). Paths with a leading `.` are treated as fully
267    /// qualified names. Paths without a leading `.` are treated as relative, and are suffix
268    /// matched on the fully qualified field name. If a Protobuf map field matches any of the
269    /// paths, a Rust `BTreeMap` field is generated instead of the default [`HashMap`][3].
270    ///
271    /// The matching is done on the Protobuf names, before converting to Rust-friendly casing
272    /// standards.
273    ///
274    /// # Examples
275    ///
276    /// ```rust
277    /// # let mut config = prost_build::Config::new();
278    /// // Match a specific field in a message type.
279    /// config.btree_map(&[".my_messages.MyMessageType.my_map_field"]);
280    ///
281    /// // Match all map fields in a message type.
282    /// config.btree_map(&[".my_messages.MyMessageType"]);
283    ///
284    /// // Match all map fields in a package.
285    /// config.btree_map(&[".my_messages"]);
286    ///
287    /// // Match all map fields. Specially useful in `no_std` contexts.
288    /// config.btree_map(&["."]);
289    ///
290    /// // Match all map fields in a nested message.
291    /// config.btree_map(&[".my_messages.MyMessageType.MyNestedMessageType"]);
292    ///
293    /// // Match all fields named 'my_map_field'.
294    /// config.btree_map(&["my_map_field"]);
295    ///
296    /// // Match all fields named 'my_map_field' in messages named 'MyMessageType', regardless of
297    /// // package or nesting.
298    /// config.btree_map(&["MyMessageType.my_map_field"]);
299    ///
300    /// // Match all fields named 'my_map_field', and all fields in the 'foo.bar' package.
301    /// config.btree_map(&["my_map_field", ".foo.bar"]);
302    /// ```
303    ///
304    /// [1]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
305    /// [2]: https://developers.google.com/protocol-buffers/docs/proto3#maps
306    /// [3]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
307    pub fn btree_map<I, S>(&mut self, paths: I) -> &mut Self
308    where
309        I: IntoIterator<Item = S>,
310        S: AsRef<str>,
311    {
312        self.map_type.clear();
313        for matcher in paths {
314            self.map_type
315                .insert(matcher.as_ref().to_string(), MapType::BTreeMap);
316        }
317        self
318    }
319
320    /// Configure the code generator to generate Rust [`bytes::Bytes`][1] fields for Protobuf
321    /// [`bytes`][2] type fields.
322    ///
323    /// # Arguments
324    ///
325    /// **`paths`** - paths to specific fields, messages, or packages which should use a Rust
326    /// `Bytes` for Protobuf `bytes` fields. Paths are specified in terms of the Protobuf type
327    /// name (not the generated Rust type name). Paths with a leading `.` are treated as fully
328    /// qualified names. Paths without a leading `.` are treated as relative, and are suffix
329    /// matched on the fully qualified field name. If a Protobuf map field matches any of the
330    /// paths, a Rust `Bytes` field is generated instead of the default [`Vec<u8>`][3].
331    ///
332    /// The matching is done on the Protobuf names, before converting to Rust-friendly casing
333    /// standards.
334    ///
335    /// # Examples
336    ///
337    /// ```rust
338    /// # let mut config = prost_build::Config::new();
339    /// // Match a specific field in a message type.
340    /// config.bytes(&[".my_messages.MyMessageType.my_bytes_field"]);
341    ///
342    /// // Match all bytes fields in a message type.
343    /// config.bytes(&[".my_messages.MyMessageType"]);
344    ///
345    /// // Match all bytes fields in a package.
346    /// config.bytes(&[".my_messages"]);
347    ///
348    /// // Match all bytes fields. Specially useful in `no_std` contexts.
349    /// config.bytes(&["."]);
350    ///
351    /// // Match all bytes fields in a nested message.
352    /// config.bytes(&[".my_messages.MyMessageType.MyNestedMessageType"]);
353    ///
354    /// // Match all fields named 'my_bytes_field'.
355    /// config.bytes(&["my_bytes_field"]);
356    ///
357    /// // Match all fields named 'my_bytes_field' in messages named 'MyMessageType', regardless of
358    /// // package or nesting.
359    /// config.bytes(&["MyMessageType.my_bytes_field"]);
360    ///
361    /// // Match all fields named 'my_bytes_field', and all fields in the 'foo.bar' package.
362    /// config.bytes(&["my_bytes_field", ".foo.bar"]);
363    /// ```
364    ///
365    /// [1]: https://docs.rs/bytes/latest/bytes/struct.Bytes.html
366    /// [2]: https://developers.google.com/protocol-buffers/docs/proto3#scalar
367    /// [3]: https://doc.rust-lang.org/std/vec/struct.Vec.html
368    pub fn bytes<I, S>(&mut self, paths: I) -> &mut Self
369    where
370        I: IntoIterator<Item = S>,
371        S: AsRef<str>,
372    {
373        self.bytes_type.clear();
374        for matcher in paths {
375            self.bytes_type
376                .insert(matcher.as_ref().to_string(), BytesType::Bytes);
377        }
378        self
379    }
380
381    /// Add additional attribute to matched fields.
382    ///
383    /// # Arguments
384    ///
385    /// **`path`** - a path matching any number of fields. These fields get the attribute.
386    /// For details about matching fields see [`btree_map`](#method.btree_map).
387    ///
388    /// **`attribute`** - an arbitrary string that'll be placed before each matched field. The
389    /// expected usage are additional attributes, usually in concert with whole-type
390    /// attributes set with [`type_attribute`](method.type_attribute), but it is not
391    /// checked and anything can be put there.
392    ///
393    /// Note that the calls to this method are cumulative ‒ if multiple paths from multiple calls
394    /// match the same field, the field gets all the corresponding attributes.
395    ///
396    /// # Examples
397    ///
398    /// ```rust
399    /// # let mut config = prost_build::Config::new();
400    /// // Prost renames fields named `in` to `in_`. But if serialized through serde,
401    /// // they should as `in`.
402    /// config.field_attribute("in", "#[serde(rename = \"in\")]");
403    /// ```
404    pub fn field_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
405    where
406        P: AsRef<str>,
407        A: AsRef<str>,
408    {
409        self.field_attributes
410            .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
411        self
412    }
413
414    /// Add additional attribute to matched messages, enums and one-ofs.
415    ///
416    /// # Arguments
417    ///
418    /// **`paths`** - a path matching any number of types. It works the same way as in
419    /// [`btree_map`](#method.btree_map), just with the field name omitted.
420    ///
421    /// **`attribute`** - an arbitrary string to be placed before each matched type. The
422    /// expected usage are additional attributes, but anything is allowed.
423    ///
424    /// The calls to this method are cumulative. They don't overwrite previous calls and if a
425    /// type is matched by multiple calls of the method, all relevant attributes are added to
426    /// it.
427    ///
428    /// For things like serde it might be needed to combine with [field
429    /// attributes](#method.field_attribute).
430    ///
431    /// # Examples
432    ///
433    /// ```rust
434    /// # let mut config = prost_build::Config::new();
435    /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
436    /// config.type_attribute(".", "#[derive(Eq)]");
437    /// // Some messages want to be serializable with serde as well.
438    /// config.type_attribute("my_messages.MyMessageType",
439    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
440    /// config.type_attribute("my_messages.MyMessageType.MyNestedMessageType",
441    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
442    /// ```
443    ///
444    /// # Oneof fields
445    ///
446    /// The `oneof` fields don't have a type name of their own inside Protobuf. Therefore, the
447    /// field name can be used both with `type_attribute` and `field_attribute` ‒ the first is
448    /// placed before the `enum` type definition, the other before the field inside corresponding
449    /// message `struct`.
450    ///
451    /// In other words, to place an attribute on the `enum` implementing the `oneof`, the match
452    /// would look like `my_messages.MyMessageType.oneofname`.
453    pub fn type_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
454    where
455        P: AsRef<str>,
456        A: AsRef<str>,
457    {
458        self.type_attributes
459            .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
460        self
461    }
462
463    /// Add additional attribute to matched messages.
464    ///
465    /// # Arguments
466    ///
467    /// **`paths`** - a path matching any number of types. It works the same way as in
468    /// [`btree_map`](#method.btree_map), just with the field name omitted.
469    ///
470    /// **`attribute`** - an arbitrary string to be placed before each matched type. The
471    /// expected usage are additional attributes, but anything is allowed.
472    ///
473    /// The calls to this method are cumulative. They don't overwrite previous calls and if a
474    /// type is matched by multiple calls of the method, all relevant attributes are added to
475    /// it.
476    ///
477    /// For things like serde it might be needed to combine with [field
478    /// attributes](#method.field_attribute).
479    ///
480    /// # Examples
481    ///
482    /// ```rust
483    /// # let mut config = prost_build::Config::new();
484    /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
485    /// config.message_attribute(".", "#[derive(Eq)]");
486    /// // Some messages want to be serializable with serde as well.
487    /// config.message_attribute("my_messages.MyMessageType",
488    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
489    /// config.message_attribute("my_messages.MyMessageType.MyNestedMessageType",
490    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
491    /// ```
492    pub fn message_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
493    where
494        P: AsRef<str>,
495        A: AsRef<str>,
496    {
497        self.message_attributes
498            .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
499        self
500    }
501
502    /// Add additional attribute to matched enums and one-ofs.
503    ///
504    /// # Arguments
505    ///
506    /// **`paths`** - a path matching any number of types. It works the same way as in
507    /// [`btree_map`](#method.btree_map), just with the field name omitted.
508    ///
509    /// **`attribute`** - an arbitrary string to be placed before each matched type. The
510    /// expected usage are additional attributes, but anything is allowed.
511    ///
512    /// The calls to this method are cumulative. They don't overwrite previous calls and if a
513    /// type is matched by multiple calls of the method, all relevant attributes are added to
514    /// it.
515    ///
516    /// For things like serde it might be needed to combine with [field
517    /// attributes](#method.field_attribute).
518    ///
519    /// # Examples
520    ///
521    /// ```rust
522    /// # let mut config = prost_build::Config::new();
523    /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
524    /// config.enum_attribute(".", "#[derive(Eq)]");
525    /// // Some messages want to be serializable with serde as well.
526    /// config.enum_attribute("my_messages.MyEnumType",
527    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
528    /// config.enum_attribute("my_messages.MyMessageType.MyNestedEnumType",
529    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
530    /// ```
531    ///
532    /// # Oneof fields
533    ///
534    /// The `oneof` fields don't have a type name of their own inside Protobuf. Therefore, the
535    /// field name can be used both with `enum_attribute` and `field_attribute` ‒ the first is
536    /// placed before the `enum` type definition, the other before the field inside corresponding
537    /// message `struct`.
538    ///
539    /// In other words, to place an attribute on the `enum` implementing the `oneof`, the match
540    /// would look like `my_messages.MyNestedMessageType.oneofname`.
541    pub fn enum_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
542    where
543        P: AsRef<str>,
544        A: AsRef<str>,
545    {
546        self.enum_attributes
547            .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
548        self
549    }
550
551    /// Configures the code generator to use the provided service generator.
552    pub fn service_generator(&mut self, service_generator: Box<dyn ServiceGenerator>) -> &mut Self {
553        self.service_generator = Some(service_generator);
554        self
555    }
556
557    /// Configures the code generator to not use the `prost_types` crate for Protobuf well-known
558    /// types, and instead generate Protobuf well-known types from their `.proto` definitions.
559    pub fn compile_well_known_types(&mut self) -> &mut Self {
560        self.prost_types = false;
561        self
562    }
563
564    /// Configures the code generator to omit documentation comments on generated Protobuf types.
565    ///
566    /// # Example
567    ///
568    /// Occasionally `.proto` files contain code blocks which are not valid Rust. To avoid doctest
569    /// failures, annotate the invalid code blocks with an [`ignore` or `no_run` attribute][1], or
570    /// disable doctests for the crate with a [Cargo.toml entry][2]. If neither of these options
571    /// are possible, then omit comments on generated code during doctest builds:
572    ///
573    /// ```rust,no_run
574    /// # fn main() -> std::io::Result<()> {
575    /// let mut config = prost_build::Config::new();
576    /// config.disable_comments(&["."]);
577    /// config.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
578    /// #     Ok(())
579    /// # }
580    /// ```
581    ///
582    /// As with other options which take a set of paths, comments can be disabled on a per-package
583    /// or per-symbol basis.
584    ///
585    /// [1]: https://doc.rust-lang.org/rustdoc/documentation-tests.html#attributes
586    /// [2]: https://doc.rust-lang.org/cargo/reference/cargo-targets.html#configuring-a-target
587    pub fn disable_comments<I, S>(&mut self, paths: I) -> &mut Self
588    where
589        I: IntoIterator<Item = S>,
590        S: AsRef<str>,
591    {
592        self.disable_comments.clear();
593        for matcher in paths {
594            self.disable_comments
595                .insert(matcher.as_ref().to_string(), ());
596        }
597        self
598    }
599
600    /// Declare an externally provided Protobuf package or type.
601    ///
602    /// `extern_path` allows `prost` types in external crates to be referenced in generated code.
603    ///
604    /// When `prost` compiles a `.proto` which includes an import of another `.proto`, it will
605    /// automatically recursively compile the imported file as well. `extern_path` can be used
606    /// to instead substitute types from an external crate.
607    ///
608    /// # Example
609    ///
610    /// As an example, consider a crate, `uuid`, with a `prost`-generated `Uuid` type:
611    ///
612    /// ```proto
613    /// // uuid.proto
614    ///
615    /// syntax = "proto3";
616    /// package uuid;
617    ///
618    /// message Uuid {
619    ///     string uuid_str = 1;
620    /// }
621    /// ```
622    ///
623    /// The `uuid` crate implements some traits for `Uuid`, and publicly exports it:
624    ///
625    /// ```rust,ignore
626    /// // lib.rs in the uuid crate
627    ///
628    /// include!(concat!(env!("OUT_DIR"), "/uuid.rs"));
629    ///
630    /// pub trait DoSomething {
631    ///     fn do_it(&self);
632    /// }
633    ///
634    /// impl DoSomething for Uuid {
635    ///     fn do_it(&self) {
636    ///         println!("Done");
637    ///     }
638    /// }
639    /// ```
640    ///
641    /// A separate crate, `my_application`, uses `prost` to generate message types which reference
642    /// `Uuid`:
643    ///
644    /// ```proto
645    /// // my_application.proto
646    ///
647    /// syntax = "proto3";
648    /// package my_application;
649    ///
650    /// import "uuid.proto";
651    ///
652    /// message MyMessage {
653    ///     uuid.Uuid message_id = 1;
654    ///     string some_payload = 2;
655    /// }
656    /// ```
657    ///
658    /// Additionally, `my_application` depends on the trait impls provided by the `uuid` crate:
659    ///
660    /// ```rust,ignore
661    /// // `main.rs` of `my_application`
662    ///
663    /// use uuid::{DoSomething, Uuid};
664    ///
665    /// include!(concat!(env!("OUT_DIR"), "/my_application.rs"));
666    ///
667    /// pub fn process_message(msg: MyMessage) {
668    ///     if let Some(uuid) = msg.message_id {
669    ///         uuid.do_it();
670    ///     }
671    /// }
672    /// ```
673    ///
674    /// Without configuring `uuid` as an external path in `my_application`'s `build.rs`, `prost`
675    /// would compile a completely separate version of the `Uuid` type, and `process_message` would
676    /// fail to compile. However, if `my_application` configures `uuid` as an extern path with a
677    /// call to `.extern_path(".uuid", "::uuid")`, `prost` will use the external type instead of
678    /// compiling a new version of `Uuid`. Note that the configuration could also be specified as
679    /// `.extern_path(".uuid.Uuid", "::uuid::Uuid")` if only the `Uuid` type were externally
680    /// provided, and not the whole `uuid` package.
681    ///
682    /// # Usage
683    ///
684    /// `extern_path` takes a fully-qualified Protobuf path, and the corresponding Rust path that
685    /// it will be substituted with in generated code. The Protobuf path can refer to a package or
686    /// a type, and the Rust path should correspondingly refer to a Rust module or type.
687    ///
688    /// ```rust
689    /// # let mut config = prost_build::Config::new();
690    /// // Declare the `uuid` Protobuf package and all nested packages and types as externally
691    /// // provided by the `uuid` crate.
692    /// config.extern_path(".uuid", "::uuid");
693    ///
694    /// // Declare the `foo.bar.baz` Protobuf package and all nested packages and types as
695    /// // externally provided by the `foo_bar_baz` crate.
696    /// config.extern_path(".foo.bar.baz", "::foo_bar_baz");
697    ///
698    /// // Declare the `uuid.Uuid` Protobuf type (and all nested types) as externally provided
699    /// // by the `uuid` crate's `Uuid` type.
700    /// config.extern_path(".uuid.Uuid", "::uuid::Uuid");
701    /// ```
702    pub fn extern_path<P1, P2>(&mut self, proto_path: P1, rust_path: P2) -> &mut Self
703    where
704        P1: Into<String>,
705        P2: Into<String>,
706    {
707        self.extern_paths
708            .push((proto_path.into(), rust_path.into()));
709        self
710    }
711
712    /// When set, the `FileDescriptorSet` generated by `protoc` is written to the provided
713    /// filesystem path.
714    ///
715    /// This option can be used in conjunction with the [`include_bytes!`] macro and the types in
716    /// the `prost-types` crate for implementing reflection capabilities, among other things.
717    ///
718    /// ## Example
719    ///
720    /// In `build.rs`:
721    ///
722    /// ```rust, no_run
723    /// # use std::env;
724    /// # use std::path::PathBuf;
725    /// # let mut config = prost_build::Config::new();
726    /// config.file_descriptor_set_path(
727    ///     PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR environment variable not set"))
728    ///         .join("file_descriptor_set.bin"));
729    /// ```
730    ///
731    /// In `lib.rs`:
732    ///
733    /// ```rust,ignore
734    /// let file_descriptor_set_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
735    /// let file_descriptor_set = prost_types::FileDescriptorSet::decode(&file_descriptor_set_bytes[..]).unwrap();
736    /// ```
737    pub fn file_descriptor_set_path<P>(&mut self, path: P) -> &mut Self
738    where
739        P: Into<PathBuf>,
740    {
741        self.file_descriptor_set_path = Some(path.into());
742        self
743    }
744
745    /// In combination with with `file_descriptor_set_path`, this can be used to provide a file
746    /// descriptor set as an input file, rather than having prost-build generate the file by calling
747    /// protoc.
748    ///
749    /// In `build.rs`:
750    ///
751    /// ```rust
752    /// # let mut config = prost_build::Config::new();
753    /// config.file_descriptor_set_path("path/from/build/system")
754    ///     .skip_protoc_run()
755    ///     .compile_protos(&["src/items.proto"], &["src/"]);
756    /// ```
757    ///
758    pub fn skip_protoc_run(&mut self) -> &mut Self {
759        self.skip_protoc_run = true;
760        self
761    }
762
763    /// Configures the code generator to not strip the enum name from variant names.
764    ///
765    /// Protobuf enum definitions commonly include the enum name as a prefix of every variant name.
766    /// This style is non-idiomatic in Rust, so by default `prost` strips the enum name prefix from
767    /// variants which include it. Configuring this option prevents `prost` from stripping the
768    /// prefix.
769    pub fn retain_enum_prefix(&mut self) -> &mut Self {
770        self.strip_enum_prefix = false;
771        self
772    }
773
774    /// Configures the output directory where generated Rust files will be written.
775    ///
776    /// If unset, defaults to the `OUT_DIR` environment variable. `OUT_DIR` is set by Cargo when
777    /// executing build scripts, so `out_dir` typically does not need to be configured.
778    pub fn out_dir<P>(&mut self, path: P) -> &mut Self
779    where
780        P: Into<PathBuf>,
781    {
782        self.out_dir = Some(path.into());
783        self
784    }
785
786    /// Configures what filename protobufs with no package definition are written to.
787    /// The filename will be appended with the `.rs` extension.
788    pub fn default_package_filename<S>(&mut self, filename: S) -> &mut Self
789    where
790        S: Into<String>,
791    {
792        self.default_package_filename = filename.into();
793        self
794    }
795
796    /// Configures the path that's used for deriving `Message` for generated messages.
797    /// This is mainly useful for generating crates that wish to re-export prost.
798    /// Defaults to `::prost::Message` if not specified.
799    pub fn prost_path<S>(&mut self, path: S) -> &mut Self
800    where
801        S: Into<String>,
802    {
803        self.prost_path = Some(path.into());
804        self
805    }
806
807    /// Add an argument to the `protoc` protobuf compilation invocation.
808    ///
809    /// # Example `build.rs`
810    ///
811    /// ```rust,no_run
812    /// # use std::io::Result;
813    /// fn main() -> Result<()> {
814    ///   let mut prost_build = prost_build::Config::new();
815    ///   // Enable a protoc experimental feature.
816    ///   prost_build.protoc_arg("--experimental_allow_proto3_optional");
817    ///   prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
818    ///   Ok(())
819    /// }
820    /// ```
821    pub fn protoc_arg<S>(&mut self, arg: S) -> &mut Self
822    where
823        S: AsRef<OsStr>,
824    {
825        self.protoc_args.push(arg.as_ref().to_owned());
826        self
827    }
828
829    /// Configures the optional module filename for easy inclusion of all generated Rust files
830    ///
831    /// If set, generates a file (inside the `OUT_DIR` or `out_dir()` as appropriate) which contains
832    /// a set of `pub mod XXX` statements combining to load all Rust files generated.  This can allow
833    /// for a shortcut where multiple related proto files have been compiled together resulting in
834    /// a semi-complex set of includes.
835    ///
836    /// Turning a need for:
837    ///
838    /// ```rust,no_run,ignore
839    /// pub mod Foo {
840    ///     pub mod Bar {
841    ///         include!(concat!(env!("OUT_DIR"), "/foo.bar.rs"));
842    ///     }
843    ///     pub mod Baz {
844    ///         include!(concat!(env!("OUT_DIR"), "/foo.baz.rs"));
845    ///     }
846    /// }
847    /// ```
848    ///
849    /// Into the simpler:
850    ///
851    /// ```rust,no_run,ignore
852    /// include!(concat!(env!("OUT_DIR"), "/_includes.rs"));
853    /// ```
854    pub fn include_file<P>(&mut self, path: P) -> &mut Self
855    where
856        P: Into<PathBuf>,
857    {
858        self.include_file = Some(path.into());
859        self
860    }
861
862    /// Configures the code generator to format the output code via `prettyplease`.
863    ///
864    /// By default, this is enabled but if the `format` feature is not enabled this does
865    /// nothing.
866    pub fn format(&mut self, enabled: bool) -> &mut Self {
867        self.fmt = enabled;
868        self
869    }
870
871    /// Compile a [`FileDescriptorSet`] into Rust files during a Cargo build with
872    /// additional code generator configuration options.
873    ///
874    /// This method is like `compile_protos` function except it does not invoke `protoc`
875    /// and instead requires the user to supply a [`FileDescriptorSet`].
876    ///
877    /// # Example `build.rs`
878    ///
879    /// ```rust,no_run
880    /// # fn fds() -> FileDescriptorSet { todo!() }
881    /// fn main() -> std::io::Result<()> {
882    ///   let file_descriptor_set = fds();
883    ///
884    ///   prost_build::Config::new()
885    ///     .compile_fds(file_descriptor_set)?;
886    /// }
887    /// ```
888    pub fn compile_fds(&mut self, fds: FileDescriptorSet) -> Result<()> {
889        let mut target_is_env = false;
890        let target: PathBuf = self.out_dir.clone().map(Ok).unwrap_or_else(|| {
891            env::var_os("OUT_DIR")
892                .ok_or_else(|| {
893                    Error::new(ErrorKind::Other, "OUT_DIR environment variable is not set")
894                })
895                .map(|val| {
896                    target_is_env = true;
897                    Into::into(val)
898                })
899        })?;
900
901        let requests = fds
902            .file
903            .into_iter()
904            .map(|descriptor| {
905                (
906                    Module::from_protobuf_package_name(descriptor.package()),
907                    descriptor,
908                )
909            })
910            .collect::<Vec<_>>();
911
912        let file_names = requests
913            .iter()
914            .map(|req| {
915                (
916                    req.0.clone(),
917                    req.0.to_file_name_or(&self.default_package_filename),
918                )
919            })
920            .collect::<HashMap<Module, String>>();
921
922        let modules = self.generate(requests)?;
923        for (module, content) in &modules {
924            let file_name = file_names
925                .get(module)
926                .expect("every module should have a filename");
927
928            let file_name = if file_name.ends_with(".rs") {
929                let check = &file_name[..file_name.len() - 3];
930                if check.contains('.') {
931                    format!("{}.rs", check.replace('.', "_"))
932                } else {
933                    file_name.to_string()
934                }
935            } else {
936                file_name.to_string()
937            };
938
939            let output_path = target.join(&file_name);
940
941            let previous_content = fs::read(&output_path);
942
943            if previous_content
944                .map(|previous_content| previous_content == content.as_bytes())
945                .unwrap_or(false)
946            {
947                trace!("unchanged: {:?}", file_name);
948            } else {
949                trace!("writing: {:?}", file_name);
950                fs::write(output_path, content)?;
951            }
952        }
953
954        if let Some(ref include_file) = self.include_file {
955            trace!("Writing include file: {:?}", target.join(include_file));
956            let mut file = fs::File::create(target.join(include_file))?;
957            self.write_includes(
958                modules.keys().collect(),
959                &mut file,
960                0,
961                if target_is_env { None } else { Some(&target) },
962            )?;
963            file.flush()?;
964        }
965
966        Ok(())
967    }
968
969    /// Compile `.proto` files into Rust files during a Cargo build with additional code generator
970    /// configuration options.
971    ///
972    /// This method is like the `prost_build::compile_protos` function, with the added ability to
973    /// specify non-default code generation options. See that function for more information about
974    /// the arguments and generated outputs.
975    ///
976    /// The `protos` and `includes` arguments are ignored if `skip_protoc_run` is specified.
977    ///
978    /// # Example `build.rs`
979    ///
980    /// ```rust,no_run
981    /// # use std::io::Result;
982    /// fn main() -> Result<()> {
983    ///   let mut prost_build = prost_build::Config::new();
984    ///   prost_build.btree_map(&["."]);
985    ///   prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
986    ///   Ok(())
987    /// }
988    /// ```
989    pub fn compile_protos(
990        &mut self,
991        protos: &[impl AsRef<Path>],
992        includes: &[impl AsRef<Path>],
993    ) -> Result<()> {
994        // TODO: This should probably emit 'rerun-if-changed=PATH' directives for cargo, however
995        // according to [1] if any are output then those paths replace the default crate root,
996        // which is undesirable. Figure out how to do it in an additive way; perhaps gcc-rs has
997        // this figured out.
998        // [1]: http://doc.crates.io/build-script.html#outputs-of-the-build-script
999
1000        let tmp;
1001        let file_descriptor_set_path = if let Some(path) = &self.file_descriptor_set_path {
1002            path.clone()
1003        } else {
1004            if self.skip_protoc_run {
1005                return Err(Error::new(
1006                    ErrorKind::Other,
1007                    "file_descriptor_set_path is required with skip_protoc_run",
1008                ));
1009            }
1010            tmp = tempfile::Builder::new().prefix("prost-build").tempdir()?;
1011            tmp.path().join("prost-descriptor-set")
1012        };
1013
1014        if !self.skip_protoc_run {
1015            let protoc = protoc_from_env();
1016
1017            let mut cmd = Command::new(protoc.clone());
1018            cmd.arg("--include_imports")
1019                .arg("--include_source_info")
1020                .arg("-o")
1021                .arg(&file_descriptor_set_path);
1022
1023            for include in includes {
1024                if include.as_ref().exists() {
1025                    cmd.arg("-I").arg(include.as_ref());
1026                } else {
1027                    debug!(
1028                        "ignoring {} since it does not exist.",
1029                        include.as_ref().display()
1030                    )
1031                }
1032            }
1033
1034            // Set the protoc include after the user includes in case the user wants to
1035            // override one of the built-in .protos.
1036            if let Some(protoc_include) = protoc_include_from_env() {
1037                cmd.arg("-I").arg(protoc_include);
1038            }
1039
1040            for arg in &self.protoc_args {
1041                cmd.arg(arg);
1042            }
1043
1044            for proto in protos {
1045                cmd.arg(proto.as_ref());
1046            }
1047
1048            debug!("Running: {:?}", cmd);
1049
1050            let output = cmd.output().map_err(|error| {
1051                Error::new(
1052                    error.kind(),
1053                    format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): (path: {:?}): {}", &protoc, error),
1054                )
1055            })?;
1056
1057            if !output.status.success() {
1058                return Err(Error::new(
1059                    ErrorKind::Other,
1060                    format!("protoc failed: {}", String::from_utf8_lossy(&output.stderr)),
1061                ));
1062            }
1063        }
1064
1065        let buf = fs::read(&file_descriptor_set_path).map_err(|e| {
1066            Error::new(
1067                e.kind(),
1068                format!(
1069                    "unable to open file_descriptor_set_path: {:?}, OS: {}",
1070                    &file_descriptor_set_path, e
1071                ),
1072            )
1073        })?;
1074        let file_descriptor_set = FileDescriptorSet::decode(&*buf).map_err(|error| {
1075            Error::new(
1076                ErrorKind::InvalidInput,
1077                format!("invalid FileDescriptorSet: {}", error),
1078            )
1079        })?;
1080
1081        self.compile_fds(file_descriptor_set)
1082    }
1083
1084    fn write_includes(
1085        &self,
1086        mut entries: Vec<&Module>,
1087        outfile: &mut fs::File,
1088        depth: usize,
1089        basepath: Option<&PathBuf>,
1090    ) -> Result<usize> {
1091        let mut written = 0;
1092        entries.sort();
1093
1094        while !entries.is_empty() {
1095            let modident = entries[0].part(depth);
1096            let matching: Vec<&Module> = entries
1097                .iter()
1098                .filter(|&v| v.part(depth) == modident)
1099                .copied()
1100                .collect();
1101            {
1102                // Will NLL sort this mess out?
1103                let _temp = entries
1104                    .drain(..)
1105                    .filter(|&v| v.part(depth) != modident)
1106                    .collect();
1107                entries = _temp;
1108            }
1109            self.write_line(outfile, depth, &format!("pub mod {} {{", modident))?;
1110            let subwritten = self.write_includes(
1111                matching
1112                    .iter()
1113                    .filter(|v| v.len() > depth + 1)
1114                    .copied()
1115                    .collect(),
1116                outfile,
1117                depth + 1,
1118                basepath,
1119            )?;
1120            written += subwritten;
1121            if subwritten != matching.len() {
1122                let modname = matching[0].to_partial_file_name(..=depth);
1123                if basepath.is_some() {
1124                    self.write_line(
1125                        outfile,
1126                        depth + 1,
1127                        &format!("include!(\"{}.rs\");", modname),
1128                    )?;
1129                } else {
1130                    self.write_line(
1131                        outfile,
1132                        depth + 1,
1133                        &format!("include!(concat!(env!(\"OUT_DIR\"), \"/{}.rs\"));", modname),
1134                    )?;
1135                }
1136                written += 1;
1137            }
1138
1139            self.write_line(outfile, depth, "}")?;
1140        }
1141        Ok(written)
1142    }
1143
1144    fn write_line(&self, outfile: &mut fs::File, depth: usize, line: &str) -> Result<()> {
1145        outfile.write_all(format!("{}{}\n", ("    ").to_owned().repeat(depth), line).as_bytes())
1146    }
1147
1148    /// Processes a set of modules and file descriptors, returning a map of modules to generated
1149    /// code contents.
1150    ///
1151    /// This is generally used when control over the output should not be managed by Prost,
1152    /// such as in a flow for a `protoc` code generating plugin. When compiling as part of a
1153    /// `build.rs` file, instead use [`compile_protos()`].
1154    pub fn generate(
1155        &mut self,
1156        requests: Vec<(Module, FileDescriptorProto)>,
1157    ) -> Result<HashMap<Module, String>> {
1158        let mut modules = HashMap::new();
1159        let mut packages = HashMap::new();
1160
1161        let message_graph = MessageGraph::new(requests.iter().map(|x| &x.1))
1162            .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?;
1163        let extern_paths = ExternPaths::new(&self.extern_paths, self.prost_types)
1164            .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?;
1165
1166        for (request_module, request_fd) in requests {
1167            // Only record packages that have services
1168            if !request_fd.service.is_empty() {
1169                packages.insert(request_module.clone(), request_fd.package().to_string());
1170            }
1171            let buf = modules
1172                .entry(request_module.clone())
1173                .or_insert_with(String::new);
1174            CodeGenerator::generate(self, &message_graph, &extern_paths, request_fd, buf);
1175            if buf.is_empty() {
1176                // Did not generate any code, remove from list to avoid inclusion in include file or output file list
1177                modules.remove(&request_module);
1178            }
1179        }
1180
1181        if let Some(ref mut service_generator) = self.service_generator {
1182            for (module, package) in packages {
1183                let buf = modules.get_mut(&module).unwrap();
1184                service_generator.finalize_package(&package, buf);
1185            }
1186        }
1187
1188        if self.fmt {
1189            self.fmt_modules(&mut modules);
1190        }
1191
1192        Ok(modules)
1193    }
1194
1195    #[cfg(feature = "format")]
1196    fn fmt_modules(&mut self, modules: &mut HashMap<Module, String>) {
1197        for buf in modules.values_mut() {
1198            let file = syn::parse_file(buf).unwrap();
1199            let formatted = prettyplease::unparse(&file);
1200            *buf = formatted;
1201        }
1202    }
1203
1204    #[cfg(not(feature = "format"))]
1205    fn fmt_modules(&mut self, _: &mut HashMap<Module, String>) {}
1206}
1207
1208impl default::Default for Config {
1209    fn default() -> Config {
1210        Config {
1211            file_descriptor_set_path: None,
1212            service_generator: None,
1213            map_type: PathMap::default(),
1214            bytes_type: PathMap::default(),
1215            type_attributes: PathMap::default(),
1216            message_attributes: PathMap::default(),
1217            enum_attributes: PathMap::default(),
1218            field_attributes: PathMap::default(),
1219            prost_types: true,
1220            strip_enum_prefix: true,
1221            out_dir: None,
1222            extern_paths: Vec::new(),
1223            default_package_filename: "_".to_string(),
1224            protoc_args: Vec::new(),
1225            disable_comments: PathMap::default(),
1226            skip_protoc_run: false,
1227            include_file: None,
1228            prost_path: None,
1229            fmt: true,
1230        }
1231    }
1232}
1233
1234impl fmt::Debug for Config {
1235    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1236        fmt.debug_struct("Config")
1237            .field("file_descriptor_set_path", &self.file_descriptor_set_path)
1238            .field("service_generator", &self.service_generator.is_some())
1239            .field("map_type", &self.map_type)
1240            .field("bytes_type", &self.bytes_type)
1241            .field("type_attributes", &self.type_attributes)
1242            .field("field_attributes", &self.field_attributes)
1243            .field("prost_types", &self.prost_types)
1244            .field("strip_enum_prefix", &self.strip_enum_prefix)
1245            .field("out_dir", &self.out_dir)
1246            .field("extern_paths", &self.extern_paths)
1247            .field("default_package_filename", &self.default_package_filename)
1248            .field("protoc_args", &self.protoc_args)
1249            .field("disable_comments", &self.disable_comments)
1250            .field("prost_path", &self.prost_path)
1251            .finish()
1252    }
1253}
1254
1255/// A Rust module path for a Protobuf package.
1256#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1257pub struct Module {
1258    components: Vec<String>,
1259}
1260
1261impl Module {
1262    /// Construct a module path from an iterator of parts.
1263    pub fn from_parts<I>(parts: I) -> Self
1264    where
1265        I: IntoIterator,
1266        I::Item: Into<String>,
1267    {
1268        Self {
1269            components: parts.into_iter().map(|s| s.into()).collect(),
1270        }
1271    }
1272
1273    /// Construct a module path from a Protobuf package name.
1274    ///
1275    /// Constituent parts are automatically converted to snake case in order to follow
1276    /// Rust module naming conventions.
1277    pub fn from_protobuf_package_name(name: &str) -> Self {
1278        Self {
1279            components: name
1280                .split('.')
1281                .filter(|s| !s.is_empty())
1282                .map(to_snake)
1283                .collect(),
1284        }
1285    }
1286
1287    /// An iterator over the parts of the path.
1288    pub fn parts(&self) -> impl Iterator<Item = &str> {
1289        self.components.iter().map(|s| s.as_str())
1290    }
1291
1292    /// Format the module path into a filename for generated Rust code.
1293    ///
1294    /// If the module path is empty, `default` is used to provide the root of the filename.
1295    pub fn to_file_name_or(&self, default: &str) -> String {
1296        let mut root = if self.components.is_empty() {
1297            default.to_owned()
1298        } else {
1299            self.components.join(".")
1300        };
1301
1302        root.push_str(".rs");
1303
1304        root
1305    }
1306
1307    /// The number of parts in the module's path.
1308    pub fn len(&self) -> usize {
1309        self.components.len()
1310    }
1311
1312    /// Whether the module's path contains any components.
1313    pub fn is_empty(&self) -> bool {
1314        self.components.is_empty()
1315    }
1316
1317    fn to_partial_file_name(&self, range: RangeToInclusive<usize>) -> String {
1318        self.components[range].join(".")
1319    }
1320
1321    fn part(&self, idx: usize) -> &str {
1322        self.components[idx].as_str()
1323    }
1324}
1325
1326impl fmt::Display for Module {
1327    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1328        let mut parts = self.parts();
1329        if let Some(first) = parts.next() {
1330            f.write_str(first)?;
1331        }
1332        for part in parts {
1333            f.write_str("::")?;
1334            f.write_str(part)?;
1335        }
1336        Ok(())
1337    }
1338}
1339
1340/// Compile `.proto` files into Rust files during a Cargo build.
1341///
1342/// The generated `.rs` files are written to the Cargo `OUT_DIR` directory, suitable for use with
1343/// the [include!][1] macro. See the [Cargo `build.rs` code generation][2] example for more info.
1344///
1345/// This function should be called in a project's `build.rs`.
1346///
1347/// # Arguments
1348///
1349/// **`protos`** - Paths to `.proto` files to compile. Any transitively [imported][3] `.proto`
1350/// files are automatically be included.
1351///
1352/// **`includes`** - Paths to directories in which to search for imports. Directories are searched
1353/// in order. The `.proto` files passed in **`protos`** must be found in one of the provided
1354/// include directories.
1355///
1356/// # Errors
1357///
1358/// This function can fail for a number of reasons:
1359///
1360///   - Failure to locate or download `protoc`.
1361///   - Failure to parse the `.proto`s.
1362///   - Failure to locate an imported `.proto`.
1363///   - Failure to compile a `.proto` without a [package specifier][4].
1364///
1365/// It's expected that this function call be `unwrap`ed in a `build.rs`; there is typically no
1366/// reason to gracefully recover from errors during a build.
1367///
1368/// # Example `build.rs`
1369///
1370/// ```rust,no_run
1371/// # use std::io::Result;
1372/// fn main() -> Result<()> {
1373///   prost_build::compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
1374///   Ok(())
1375/// }
1376/// ```
1377///
1378/// [1]: https://doc.rust-lang.org/std/macro.include.html
1379/// [2]: http://doc.crates.io/build-script.html#case-study-code-generation
1380/// [3]: https://developers.google.com/protocol-buffers/docs/proto3#importing-definitions
1381/// [4]: https://developers.google.com/protocol-buffers/docs/proto#packages
1382pub fn compile_protos(protos: &[impl AsRef<Path>], includes: &[impl AsRef<Path>]) -> Result<()> {
1383    Config::new().compile_protos(protos, includes)
1384}
1385
1386/// Compile a [`FileDescriptorSet`] into Rust files during a Cargo build.
1387///
1388/// The generated `.rs` files are written to the Cargo `OUT_DIR` directory, suitable for use with
1389/// the [include!][1] macro. See the [Cargo `build.rs` code generation][2] example for more info.
1390///
1391/// This function should be called in a project's `build.rs`.
1392///
1393/// This function can be combined with a crate like [`protox`] which outputs a
1394/// [`FileDescriptorSet`] and is a pure Rust implementation of `protoc`.
1395///
1396/// [`protox`]: https://github.com/andrewhickman/protox
1397///
1398/// # Example
1399/// ```rust,no_run
1400/// # fn fds() -> FileDescriptorSet { todo!() }
1401/// fn main() -> std::io::Result<()> {
1402///   let file_descriptor_set = fds();
1403///
1404///   prost_build::compile_fds(file_descriptor_set)?;
1405/// }
1406/// ```
1407pub fn compile_fds(fds: FileDescriptorSet) -> Result<()> {
1408    Config::new().compile_fds(fds)
1409}
1410
1411/// Returns the path to the `protoc` binary.
1412pub fn protoc_from_env() -> PathBuf {
1413    let os_specific_hint = if cfg!(target_os = "macos") {
1414        "You could try running `brew install protobuf` or downloading it from https://github.com/protocolbuffers/protobuf/releases"
1415    } else if cfg!(target_os = "linux") {
1416        "If you're on debian, try `apt-get install protobuf-compiler` or download it from https://github.com/protocolbuffers/protobuf/releases"
1417    } else {
1418        "You can download it from https://github.com/protocolbuffers/protobuf/releases or from your package manager."
1419    };
1420    let error_msg =
1421        "Could not find `protoc` installation and this build crate cannot proceed without
1422    this knowledge. If `protoc` is installed and this crate had trouble finding
1423    it, you can set the `PROTOC` environment variable with the specific path to your
1424    installed `protoc` binary.";
1425    let msg = format!(
1426        "{}{}
1427
1428For more information: https://docs.rs/prost-build/#sourcing-protoc
1429",
1430        error_msg, os_specific_hint
1431    );
1432
1433    env::var_os("PROTOC")
1434        .map(PathBuf::from)
1435        .or_else(|| which::which("protoc").ok())
1436        .expect(&msg)
1437}
1438
1439/// Returns the path to the Protobuf include directory.
1440pub fn protoc_include_from_env() -> Option<PathBuf> {
1441    let protoc_include: PathBuf = env::var_os("PROTOC_INCLUDE")?.into();
1442
1443    if !protoc_include.exists() {
1444        panic!(
1445            "PROTOC_INCLUDE environment variable points to non-existent directory ({:?})",
1446            protoc_include
1447        );
1448    }
1449    if !protoc_include.is_dir() {
1450        panic!(
1451            "PROTOC_INCLUDE environment variable points to a non-directory file ({:?})",
1452            protoc_include
1453        );
1454    }
1455
1456    Some(protoc_include)
1457}
1458
1459#[cfg(test)]
1460mod tests {
1461    use std::cell::RefCell;
1462    use std::fs::File;
1463    use std::io::Read;
1464    use std::path::Path;
1465    use std::rc::Rc;
1466
1467    use super::*;
1468
1469    /// An example service generator that generates a trait with methods corresponding to the
1470    /// service methods.
1471    struct ServiceTraitGenerator;
1472
1473    impl ServiceGenerator for ServiceTraitGenerator {
1474        fn generate(&mut self, service: Service, buf: &mut String) {
1475            // Generate a trait for the service.
1476            service.comments.append_with_indent(0, buf);
1477            buf.push_str(&format!("trait {} {{\n", &service.name));
1478
1479            // Generate the service methods.
1480            for method in service.methods {
1481                method.comments.append_with_indent(1, buf);
1482                buf.push_str(&format!(
1483                    "    fn {}(_: {}) -> {};\n",
1484                    method.name, method.input_type, method.output_type
1485                ));
1486            }
1487
1488            // Close out the trait.
1489            buf.push_str("}\n");
1490        }
1491        fn finalize(&mut self, buf: &mut String) {
1492            // Needs to be present only once, no matter how many services there are
1493            buf.push_str("pub mod utils { }\n");
1494        }
1495    }
1496
1497    /// Implements `ServiceGenerator` and provides some state for assertions.
1498    struct MockServiceGenerator {
1499        state: Rc<RefCell<MockState>>,
1500    }
1501
1502    /// Holds state for `MockServiceGenerator`
1503    #[derive(Default)]
1504    struct MockState {
1505        service_names: Vec<String>,
1506        package_names: Vec<String>,
1507        finalized: u32,
1508    }
1509
1510    impl MockServiceGenerator {
1511        fn new(state: Rc<RefCell<MockState>>) -> Self {
1512            Self { state }
1513        }
1514    }
1515
1516    impl ServiceGenerator for MockServiceGenerator {
1517        fn generate(&mut self, service: Service, _buf: &mut String) {
1518            let mut state = self.state.borrow_mut();
1519            state.service_names.push(service.name);
1520        }
1521
1522        fn finalize(&mut self, _buf: &mut String) {
1523            let mut state = self.state.borrow_mut();
1524            state.finalized += 1;
1525        }
1526
1527        fn finalize_package(&mut self, package: &str, _buf: &mut String) {
1528            let mut state = self.state.borrow_mut();
1529            state.package_names.push(package.to_string());
1530        }
1531    }
1532
1533    #[test]
1534    fn smoke_test() {
1535        let _ = env_logger::try_init();
1536        Config::new()
1537            .service_generator(Box::new(ServiceTraitGenerator))
1538            .out_dir(std::env::temp_dir())
1539            .compile_protos(&["src/fixtures/smoke_test/smoke_test.proto"], &["src"])
1540            .unwrap();
1541    }
1542
1543    #[test]
1544    fn finalize_package() {
1545        let _ = env_logger::try_init();
1546
1547        let state = Rc::new(RefCell::new(MockState::default()));
1548        let gen = MockServiceGenerator::new(Rc::clone(&state));
1549
1550        Config::new()
1551            .service_generator(Box::new(gen))
1552            .include_file("_protos.rs")
1553            .out_dir(std::env::temp_dir())
1554            .compile_protos(
1555                &[
1556                    "src/fixtures/helloworld/hello.proto",
1557                    "src/fixtures/helloworld/goodbye.proto",
1558                ],
1559                &["src/fixtures/helloworld"],
1560            )
1561            .unwrap();
1562
1563        let state = state.borrow();
1564        assert_eq!(&state.service_names, &["Greeting", "Farewell"]);
1565        assert_eq!(&state.package_names, &["helloworld"]);
1566        assert_eq!(state.finalized, 3);
1567    }
1568
1569    #[test]
1570    fn test_generate_message_attributes() {
1571        let _ = env_logger::try_init();
1572
1573        let out_dir = std::env::temp_dir();
1574
1575        Config::new()
1576            .out_dir(out_dir.clone())
1577            .message_attribute(".", "#[derive(derive_builder::Builder)]")
1578            .enum_attribute(".", "#[some_enum_attr(u8)]")
1579            .compile_protos(
1580                &["src/fixtures/helloworld/hello.proto"],
1581                &["src/fixtures/helloworld"],
1582            )
1583            .unwrap();
1584
1585        let out_file = out_dir
1586            .join("helloworld.rs")
1587            .as_path()
1588            .display()
1589            .to_string();
1590        let expected_content = read_all_content("src/fixtures/helloworld/_expected_helloworld.rs")
1591            .replace("\r\n", "\n");
1592        let content = read_all_content(&out_file).replace("\r\n", "\n");
1593        assert_eq!(
1594            expected_content, content,
1595            "Unexpected content: \n{}",
1596            content
1597        );
1598    }
1599
1600    #[test]
1601    fn test_generate_no_empty_outputs() {
1602        let _ = env_logger::try_init();
1603        let state = Rc::new(RefCell::new(MockState::default()));
1604        let gen = MockServiceGenerator::new(Rc::clone(&state));
1605        let include_file = "_include.rs";
1606        let out_dir = std::env::temp_dir()
1607            .as_path()
1608            .join("test_generate_no_empty_outputs");
1609        let previously_empty_proto_path = out_dir.as_path().join(Path::new("google.protobuf.rs"));
1610        // For reproducibility, ensure we start with the out directory created and empty
1611        let _ = fs::remove_dir_all(&out_dir);
1612        let _ = fs::create_dir(&out_dir);
1613
1614        Config::new()
1615            .service_generator(Box::new(gen))
1616            .include_file(include_file)
1617            .out_dir(&out_dir)
1618            .compile_protos(
1619                &["src/fixtures/imports_empty/imports_empty.proto"],
1620                &["src/fixtures/imports_empty"],
1621            )
1622            .unwrap();
1623
1624        // Prior to PR introducing this test, the generated include file would have the file
1625        // google.protobuf.rs which was an empty file. Now that file should only exist if it has content
1626        if let Ok(mut f) = File::open(&previously_empty_proto_path) {
1627            // Since this file was generated, it should not be empty.
1628            let mut contents = String::new();
1629            f.read_to_string(&mut contents).unwrap();
1630            assert!(!contents.is_empty());
1631        } else {
1632            // The file wasn't generated so the result include file should not reference it
1633            let expected = read_all_content("src/fixtures/imports_empty/_expected_include.rs");
1634            let actual = read_all_content(
1635                out_dir
1636                    .as_path()
1637                    .join(Path::new(include_file))
1638                    .display()
1639                    .to_string()
1640                    .as_str(),
1641            );
1642            // Normalizes windows and Linux-style EOL
1643            let expected = expected.replace("\r\n", "\n");
1644            let actual = actual.replace("\r\n", "\n");
1645            assert_eq!(expected, actual);
1646        }
1647    }
1648
1649    #[test]
1650    fn deterministic_include_file() {
1651        let _ = env_logger::try_init();
1652
1653        for _ in 1..10 {
1654            let state = Rc::new(RefCell::new(MockState::default()));
1655            let gen = MockServiceGenerator::new(Rc::clone(&state));
1656            let include_file = "_include.rs";
1657            let tmp_dir = std::env::temp_dir();
1658
1659            Config::new()
1660                .service_generator(Box::new(gen))
1661                .include_file(include_file)
1662                .out_dir(std::env::temp_dir())
1663                .compile_protos(
1664                    &[
1665                        "src/fixtures/alphabet/a.proto",
1666                        "src/fixtures/alphabet/b.proto",
1667                        "src/fixtures/alphabet/c.proto",
1668                        "src/fixtures/alphabet/d.proto",
1669                        "src/fixtures/alphabet/e.proto",
1670                        "src/fixtures/alphabet/f.proto",
1671                    ],
1672                    &["src/fixtures/alphabet"],
1673                )
1674                .unwrap();
1675
1676            let expected = read_all_content("src/fixtures/alphabet/_expected_include.rs");
1677            let actual = read_all_content(
1678                tmp_dir
1679                    .as_path()
1680                    .join(Path::new(include_file))
1681                    .display()
1682                    .to_string()
1683                    .as_str(),
1684            );
1685            // Normalizes windows and Linux-style EOL
1686            let expected = expected.replace("\r\n", "\n");
1687            let actual = actual.replace("\r\n", "\n");
1688
1689            assert_eq!(expected, actual);
1690        }
1691    }
1692
1693    fn read_all_content(filepath: &str) -> String {
1694        let mut f = File::open(filepath).unwrap();
1695        let mut content = String::new();
1696        f.read_to_string(&mut content).unwrap();
1697        content
1698    }
1699}