Skip to main content

ntex_prost_build/
lib.rs

1#![doc(html_root_url = "https://docs.rs/ntex-prost-build/0.11.0")]
2#![allow(clippy::option_as_ref_deref)]
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//! pub mod items {
66//!     include!(concat!(env!("OUT_DIR"), "/snazzy.items.rs"));
67//! }
68//!
69//! pub fn create_large_shirt(color: String) -> items::Shirt {
70//!     let mut shirt = items::Shirt::default();
71//!     shirt.color = color;
72//!     shirt.set_size(items::shirt::Size::Large);
73//!     shirt
74//! }
75//! ```
76//!
77//! That's it! Run `cargo doc` to see documentation for the generated code. The full
78//! example project can be found on [GitHub](https://github.com/danburkert/snazzy).
79//!
80//! ### Cleaning up Markdown in code docs
81//!
82//! If you are using protobuf files from third parties, where the author of the protobuf
83//! is not treating comments as Markdown, or is, but has codeblocks in their docs,
84//! then you may need to clean up the documentation in order that `cargo test --doc`
85//! will not fail spuriously, and that `cargo doc` doesn't attempt to render the
86//! codeblocks as Rust code.
87//!
88//! To do this, in your `Cargo.toml`, add `features = ["cleanup-markdown"]` to the inclusion
89//! of the `prost-build` crate and when your code is generated, the code docs will automatically
90//! be cleaned up a bit.
91//!
92//! ## Sourcing `protoc`
93//!
94//! `prost-build` depends on the Protocol Buffers compiler, `protoc`, to parse `.proto` files into
95//! a representation that can be transformed into Rust. If set, `prost-build` uses the `PROTOC` and
96//! `PROTOC_INCLUDE` environment variables for locating `protoc` and the Protobuf includes
97//! directory. For example, on a macOS system where Protobuf is installed with Homebrew, set the
98//! environment to:
99//!
100//! ```bash
101//! PROTOC=/usr/local/bin/protoc
102//! PROTOC_INCLUDE=/usr/local/include
103//! ```
104//!
105//! and in a typical Linux installation:
106//!
107//! ```bash
108//! PROTOC=/usr/bin/protoc
109//! PROTOC_INCLUDE=/usr/include
110//! ```
111//!
112//! If no `PROTOC` environment variable is set then `prost-build` will search the
113//! current path for `protoc` or `protoc.exe`. If `protoc` is not found via these
114//! two methods then `prost-build` will attempt to compile `protoc` from the bundled
115//! source.
116//!
117//! If you would not like `prost-build` to not compile `protoc` from source ever then
118//! ensure you have set `PROTOC_NO_VENDOR` environment variable as this will disable
119//! compiling from source even if the `vendored` feature flag is enabled.
120//!
121//! If you would like to always compile from source then setting the `vendored` feature
122//! flag will force `prost-build` to always build `protoc` from source.
123//!
124//! If `PROTOC_INCLUDE` is not found in the environment, then the Protobuf include directory
125//! bundled in the prost-build crate is be used.
126//!
127//! ### Compiling `protoc` from source
128//!
129//! Compiling `protoc` from source requires a few external dependencies. Currently,
130//! `prost-build` uses `cmake` to build `protoc`. For more information check out the
131//! [protobuf build instructions][protobuf-build].
132//!
133//! [protobuf-build]: https://github.com/protocolbuffers/protobuf/blob/master/src/README.md
134
135mod ast;
136mod code_generator;
137mod extern_paths;
138mod ident;
139mod path;
140
141use std::ffi::{OsStr, OsString};
142use std::io::{Error, ErrorKind, Result, Write};
143use std::ops::RangeToInclusive;
144use std::path::{Path, PathBuf};
145use std::{collections::HashMap, default, env, fmt, fs, process::Command};
146
147use log::trace;
148use prost::Message;
149use prost_types::{FileDescriptorProto, FileDescriptorSet};
150
151pub use crate::ast::{Comments, Method, Service};
152use crate::code_generator::CodeGenerator;
153use crate::extern_paths::ExternPaths;
154use crate::ident::to_snake;
155use crate::path::PathMap;
156
157/// A service generator takes a service descriptor and generates Rust code.
158///
159/// `ServiceGenerator` can be used to generate application-specific interfaces
160/// or implementations for Protobuf service definitions.
161///
162/// Service generators are registered with a code generator using the
163/// `Config::service_generator` method.
164///
165/// A viable scenario is that an RPC framework provides a service generator. It generates a trait
166/// describing methods of the service and some glue code to call the methods of the trait, defining
167/// details like how errors are handled or if it is asynchronous. Then the user provides an
168/// implementation of the generated trait in the application code and plugs it into the framework.
169///
170/// Such framework isn't part of Prost at present.
171pub trait ServiceGenerator {
172    /// Generates a Rust interface or implementation for a service, writing the
173    /// result to `buf`.
174    fn generate(&mut self, service: Service, buf: &mut String, priv_buf: &mut String);
175
176    /// Finalizes the generation process.
177    ///
178    /// In case there's something that needs to be output at the end of the generation process, it
179    /// goes here. Similar to [`generate`](#method.generate), the output should be appended to
180    /// `buf`.
181    ///
182    /// An example can be a module or other thing that needs to appear just once, not for each
183    /// service generated.
184    ///
185    /// This still can be called multiple times in a lifetime of the service generator, because it
186    /// is called once per `.proto` file.
187    ///
188    /// The default implementation is empty and does nothing.
189    fn finalize(&mut self, _buf: &mut String) {}
190
191    /// Finalizes the generation process for an entire protobuf package.
192    ///
193    /// This differs from [`finalize`](#method.finalize) by where (and how often) it is called
194    /// during the service generator life cycle. This method is called once per protobuf package,
195    /// making it ideal for grouping services within a single package spread across multiple
196    /// `.proto` files.
197    ///
198    /// The default implementation is empty and does nothing.
199    fn finalize_package(&mut self, _package: &str, _buf: &mut String) {}
200}
201
202/// Configuration options for Protobuf code generation.
203///
204/// This configuration builder can be used to set non-default code generation options.
205pub struct Config {
206    file_descriptor_set_path: Option<PathBuf>,
207    service_generator: Option<Box<dyn ServiceGenerator>>,
208    types_map: PathMap<String>,
209    type_attributes: PathMap<String>,
210    field_attributes: PathMap<String>,
211    prost_types: bool,
212    strip_enum_prefix: bool,
213    out_dir: Option<PathBuf>,
214    extern_paths: Vec<(String, String)>,
215    default_package_filename: String,
216    protoc_args: Vec<OsString>,
217    disable_comments: PathMap<()>,
218    skip_protoc_run: bool,
219    include_file: Option<PathBuf>,
220}
221
222impl Config {
223    /// Creates a new code generator configuration with default options.
224    pub fn new() -> Config {
225        Config::default()
226    }
227
228    /// Configure the code generator to generate Rust type fields for Protobuf fields.
229    ///
230    /// # Arguments
231    ///
232    /// **`paths`** - paths to specific fields, messages, or packages which should use a Rust
233    /// types that implements `NativeType` trait. Paths are specified in terms of the Protobuf type
234    /// name (not the generated Rust type name). Paths with a leading `.` are treated as fully
235    /// qualified names. Paths without a leading `.` are treated as relative, and are suffix
236    /// matched on the fully qualified field name. If a Protobuf map field matches any of the
237    /// paths, a Rust field is generated instead of the default types.
238    ///
239    /// The matching is done on the Protobuf names, before converting to Rust-friendly casing
240    /// standards.
241    ///
242    /// # Examples
243    ///
244    /// ```rust
245    /// # let mut config = prost_build::Config::new();
246    /// // Match a specific field in a message type.
247    /// config.map_field_type(&[".my_messages.MyMessageType.my_bytes_field"], "MyCustomType");
248    ///
249    /// // Match all fields in a package.
250    /// config.map_field_type(&[".my_messages"], "MyCustomType");
251    ///
252    /// // Match all fields named 'my_bytes_field'.
253    /// config.map_field_type(&["my_bytes_field"], "Bytes");
254    ///
255    /// // Match all fields named 'my_bytes_field' in messages named 'MyMessageType', regardless of
256    /// // package or nesting.
257    /// config.map_field_type(&["MyMessageType.my_bytes_field"], "Bytes");
258    ///
259    /// // Match all fields named 'my_bytes_field', and all fields in the 'foo.bar' package.
260    /// config.map_field_type(&["my_field", "::foo::bar::CustomType"]);
261    /// ```
262    pub fn map_field_type<I, S>(&mut self, paths: I, tp: &str) -> &mut Self
263    where
264        I: IntoIterator<Item = S>,
265        S: AsRef<str>,
266    {
267        for matcher in paths {
268            self.types_map
269                .insert(matcher.as_ref().to_string(), tp.to_string());
270        }
271        self
272    }
273
274    /// Add additional attribute to matched fields.
275    ///
276    /// # Arguments
277    ///
278    /// **`path`** - a path matching any number of fields. These fields get the attribute.
279    /// For details about matching fields see [`btree_map`](#method.btree_map).
280    ///
281    /// **`attribute`** - an arbitrary string that'll be placed before each matched field. The
282    /// expected usage are additional attributes, usually in concert with whole-type
283    /// attributes set with [`type_attribute`](method.type_attribute), but it is not
284    /// checked and anything can be put there.
285    ///
286    /// Note that the calls to this method are cumulative ‒ if multiple paths from multiple calls
287    /// match the same field, the field gets all the corresponding attributes.
288    ///
289    /// # Examples
290    ///
291    /// ```rust
292    /// # let mut config = prost_build::Config::new();
293    /// // Prost renames fields named `in` to `in_`. But if serialized through serde,
294    /// // they should as `in`.
295    /// config.field_attribute("in", "#[serde(rename = \"in\")]");
296    /// ```
297    pub fn field_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
298    where
299        P: AsRef<str>,
300        A: AsRef<str>,
301    {
302        self.field_attributes
303            .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
304        self
305    }
306
307    /// Add additional attribute to matched messages, 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`](#method.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](#method.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.type_attribute(".", "#[derive(Eq)]");
330    /// // Some messages want to be serializable with serde as well.
331    /// config.type_attribute("my_messages.MyMessageType",
332    ///                       "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
333    /// config.type_attribute("my_messages.MyMessageType.MyNestedMessageType",
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 `type_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.MyMessageType.oneofname`.
346    pub fn type_attribute<P, A>(&mut self, path: P, attribute: A) -> &mut Self
347    where
348        P: AsRef<str>,
349        A: AsRef<str>,
350    {
351        self.type_attributes
352            .insert(path.as_ref().to_string(), attribute.as_ref().to_string());
353        self
354    }
355
356    /// Configures the code generator to use the provided service generator.
357    pub fn service_generator(
358        &mut self,
359        service_generator: Box<dyn ServiceGenerator>,
360    ) -> &mut Self {
361        self.service_generator = Some(service_generator);
362        self
363    }
364
365    /// Configures the code generator to not use the `prost_types` crate for Protobuf well-known
366    /// types, and instead generate Protobuf well-known types from their `.proto` definitions.
367    pub fn compile_well_known_types(&mut self) -> &mut Self {
368        self.prost_types = false;
369        self
370    }
371
372    /// Configures the code generator to omit documentation comments on generated Protobuf types.
373    ///
374    /// # Example
375    ///
376    /// Occasionally `.proto` files contain code blocks which are not valid Rust. To avoid doctest
377    /// failures, annotate the invalid code blocks with an [`ignore` or `no_run` attribute][1], or
378    /// disable doctests for the crate with a [Cargo.toml entry][2]. If neither of these options
379    /// are possible, then omit comments on generated code during doctest builds:
380    ///
381    /// ```rust,ignore
382    /// let mut config = prost_build::Config::new();
383    /// config.disable_comments(".");
384    /// config.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
385    /// ```
386    ///
387    /// As with other options which take a set of paths, comments can be disabled on a per-package
388    /// or per-symbol basis.
389    ///
390    /// [1]: https://doc.rust-lang.org/rustdoc/documentation-tests.html#attributes
391    /// [2]: https://doc.rust-lang.org/cargo/reference/cargo-targets.html#configuring-a-target
392    pub fn disable_comments<I, S>(&mut self, paths: I) -> &mut Self
393    where
394        I: IntoIterator<Item = S>,
395        S: AsRef<str>,
396    {
397        self.disable_comments.clear();
398        for matcher in paths {
399            self.disable_comments
400                .insert(matcher.as_ref().to_string(), ());
401        }
402        self
403    }
404
405    /// Declare an externally provided Protobuf package or type.
406    ///
407    /// `extern_path` allows `prost` types in external crates to be referenced in generated code.
408    ///
409    /// When `prost` compiles a `.proto` which includes an import of another `.proto`, it will
410    /// automatically recursively compile the imported file as well. `extern_path` can be used
411    /// to instead substitute types from an external crate.
412    ///
413    /// # Example
414    ///
415    /// As an example, consider a crate, `uuid`, with a `prost`-generated `Uuid` type:
416    ///
417    /// ```proto
418    /// // uuid.proto
419    ///
420    /// syntax = "proto3";
421    /// package uuid;
422    ///
423    /// message Uuid {
424    ///     string uuid_str = 1;
425    /// }
426    /// ```
427    ///
428    /// The `uuid` crate implements some traits for `Uuid`, and publicly exports it:
429    ///
430    /// ```rust,ignore
431    /// // lib.rs in the uuid crate
432    ///
433    /// include!(concat!(env!("OUT_DIR"), "/uuid.rs"));
434    ///
435    /// pub trait DoSomething {
436    ///     fn do_it(&self);
437    /// }
438    ///
439    /// impl DoSomething for Uuid {
440    ///     fn do_it(&self) {
441    ///         println!("Done");
442    ///     }
443    /// }
444    /// ```
445    ///
446    /// A separate crate, `my_application`, uses `prost` to generate message types which reference
447    /// `Uuid`:
448    ///
449    /// ```proto
450    /// // my_application.proto
451    ///
452    /// syntax = "proto3";
453    /// package my_application;
454    ///
455    /// import "uuid.proto";
456    ///
457    /// message MyMessage {
458    ///     uuid.Uuid message_id = 1;
459    ///     string some_payload = 2;
460    /// }
461    /// ```
462    ///
463    /// Additionally, `my_application` depends on the trait impls provided by the `uuid` crate:
464    ///
465    /// ```rust,ignore
466    /// // `main.rs` of `my_application`
467    ///
468    /// use uuid::{DoSomething, Uuid};
469    ///
470    /// include!(concat!(env!("OUT_DIR"), "/my_application.rs"));
471    ///
472    /// pub fn process_message(msg: MyMessage) {
473    ///     if let Some(uuid) = msg.message_id {
474    ///         uuid.do_it();
475    ///     }
476    /// }
477    /// ```
478    ///
479    /// Without configuring `uuid` as an external path in `my_application`'s `build.rs`, `prost`
480    /// would compile a completely separate version of the `Uuid` type, and `process_message` would
481    /// fail to compile. However, if `my_application` configures `uuid` as an extern path with a
482    /// call to `.extern_path(".uuid", "::uuid")`, `prost` will use the external type instead of
483    /// compiling a new version of `Uuid`. Note that the configuration could also be specified as
484    /// `.extern_path(".uuid.Uuid", "::uuid::Uuid")` if only the `Uuid` type were externally
485    /// provided, and not the whole `uuid` package.
486    ///
487    /// # Usage
488    ///
489    /// `extern_path` takes a fully-qualified Protobuf path, and the corresponding Rust path that
490    /// it will be substituted with in generated code. The Protobuf path can refer to a package or
491    /// a type, and the Rust path should correspondingly refer to a Rust module or type.
492    ///
493    /// ```rust
494    /// # let mut config = prost_build::Config::new();
495    /// // Declare the `uuid` Protobuf package and all nested packages and types as externally
496    /// // provided by the `uuid` crate.
497    /// config.extern_path(".uuid", "::uuid");
498    ///
499    /// // Declare the `foo.bar.baz` Protobuf package and all nested packages and types as
500    /// // externally provided by the `foo_bar_baz` crate.
501    /// config.extern_path(".foo.bar.baz", "::foo_bar_baz");
502    ///
503    /// // Declare the `uuid.Uuid` Protobuf type (and all nested types) as externally provided
504    /// // by the `uuid` crate's `Uuid` type.
505    /// config.extern_path(".uuid.Uuid", "::uuid::Uuid");
506    /// ```
507    pub fn extern_path<P1, P2>(&mut self, proto_path: P1, rust_path: P2) -> &mut Self
508    where
509        P1: Into<String>,
510        P2: Into<String>,
511    {
512        self.extern_paths
513            .push((proto_path.into(), rust_path.into()));
514        self
515    }
516
517    /// When set, the `FileDescriptorSet` generated by `protoc` is written to the provided
518    /// filesystem path.
519    ///
520    /// This option can be used in conjunction with the [`include_bytes!`] macro and the types in
521    /// the `prost-types` crate for implementing reflection capabilities, among other things.
522    ///
523    /// ## Example
524    ///
525    /// In `build.rs`:
526    ///
527    /// ```rust
528    /// # use std::env;
529    /// # use std::path::PathBuf;
530    /// # let mut config = prost_build::Config::new();
531    /// config.file_descriptor_set_path(
532    ///     PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR environment variable not set"))
533    ///         .join("file_descriptor_set.bin"));
534    /// ```
535    ///
536    /// In `lib.rs`:
537    ///
538    /// ```rust,ignore
539    /// let file_descriptor_set_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
540    /// let file_descriptor_set = prost_types::FileDescriptorSet::decode(&file_descriptor_set_bytes[..]).unwrap();
541    /// ```
542    pub fn file_descriptor_set_path<P>(&mut self, path: P) -> &mut Self
543    where
544        P: Into<PathBuf>,
545    {
546        self.file_descriptor_set_path = Some(path.into());
547        self
548    }
549
550    /// In combination with with `file_descriptor_set_path`, this can be used to provide a file
551    /// descriptor set as an input file, rather than having prost-build generate the file by calling
552    /// protoc.  Prost-build does require that the descriptor set was generated with
553    /// --include_source_info.
554    ///
555    /// In `build.rs`:
556    ///
557    /// ```rust
558    /// # let mut config = prost_build::Config::new();
559    /// config.file_descriptor_set_path("path/from/build/system")
560    ///     .skip_protoc_run()
561    ///     .compile_protos(&["src/items.proto"], &["src/"]);
562    /// ```
563    ///
564    pub fn skip_protoc_run(&mut self) -> &mut Self {
565        self.skip_protoc_run = true;
566        self
567    }
568
569    /// Configures the code generator to not strip the enum name from variant names.
570    ///
571    /// Protobuf enum definitions commonly include the enum name as a prefix of every variant name.
572    /// This style is non-idiomatic in Rust, so by default `prost` strips the enum name prefix from
573    /// variants which include it. Configuring this option prevents `prost` from stripping the
574    /// prefix.
575    pub fn retain_enum_prefix(&mut self) -> &mut Self {
576        self.strip_enum_prefix = false;
577        self
578    }
579
580    /// Configures the output directory where generated Rust files will be written.
581    ///
582    /// If unset, defaults to the `OUT_DIR` environment variable. `OUT_DIR` is set by Cargo when
583    /// executing build scripts, so `out_dir` typically does not need to be configured.
584    pub fn out_dir<P>(&mut self, path: P) -> &mut Self
585    where
586        P: Into<PathBuf>,
587    {
588        self.out_dir = Some(path.into());
589        self
590    }
591
592    /// Configures what filename protobufs with no package definition are written to.
593    pub fn default_package_filename<S>(&mut self, filename: S) -> &mut Self
594    where
595        S: Into<String>,
596    {
597        self.default_package_filename = filename.into();
598        self
599    }
600
601    /// Add an argument to the `protoc` protobuf compilation invocation.
602    ///
603    /// # Example `build.rs`
604    ///
605    /// ```rust,no_run
606    /// # use std::io::Result;
607    /// fn main() -> Result<()> {
608    ///   let mut prost_build = prost_build::Config::new();
609    ///   // Enable a protoc experimental feature.
610    ///   prost_build.protoc_arg("--experimental_allow_proto3_optional");
611    ///   prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
612    ///   Ok(())
613    /// }
614    /// ```
615    pub fn protoc_arg<S>(&mut self, arg: S) -> &mut Self
616    where
617        S: AsRef<OsStr>,
618    {
619        self.protoc_args.push(arg.as_ref().to_owned());
620        self
621    }
622
623    /// Configures the optional module filename for easy inclusion of all generated Rust files
624    ///
625    /// If set, generates a file (inside the `OUT_DIR` or `out_dir()` as appropriate) which contains
626    /// a set of `pub mod XXX` statements combining to load all Rust files generated.  This can allow
627    /// for a shortcut where multiple related proto files have been compiled together resulting in
628    /// a semi-complex set of includes.
629    ///
630    /// Turning a need for:
631    ///
632    /// ```rust,no_run,ignore
633    /// pub mod Foo {
634    ///     pub mod Bar {
635    ///         include!(concat!(env!("OUT_DIR"), "/foo.bar.rs"));
636    ///     }
637    ///     pub mod Baz {
638    ///         include!(concat!(env!("OUT_DIR"), "/foo.baz.rs"));
639    ///     }
640    /// }
641    /// ```
642    ///
643    /// Into the simpler:
644    ///
645    /// ```rust,no_run,ignore
646    /// include!(concat!(env!("OUT_DIR"), "/_includes.rs"));
647    /// ```
648    pub fn include_file<P>(&mut self, path: P) -> &mut Self
649    where
650        P: Into<PathBuf>,
651    {
652        self.include_file = Some(path.into());
653        self
654    }
655
656    /// Compile `.proto` files into Rust files during a Cargo build with additional code generator
657    /// configuration options.
658    ///
659    /// This method is like the `prost_build::compile_protos` function, with the added ability to
660    /// specify non-default code generation options. See that function for more information about
661    /// the arguments and generated outputs.
662    ///
663    /// The `protos` and `includes` arguments are ignored if `skip_protoc_run` is specified.
664    ///
665    /// # Example `build.rs`
666    ///
667    /// ```rust,no_run
668    /// # use std::io::Result;
669    /// fn main() -> Result<()> {
670    ///   let mut prost_build = prost_build::Config::new();
671    ///   prost_build.btree_map(&["."]);
672    ///   prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
673    ///   Ok(())
674    /// }
675    /// ```
676    pub fn compile_protos(
677        &mut self,
678        protos: &[impl AsRef<Path>],
679        includes: &[impl AsRef<Path>],
680    ) -> Result<()> {
681        let mut target_is_env = false;
682        let target: PathBuf = self.out_dir.clone().map(Ok).unwrap_or_else(|| {
683            env::var_os("OUT_DIR")
684                .ok_or_else(|| Error::other("OUT_DIR environment variable is not set"))
685                .map(|val| {
686                    target_is_env = true;
687                    Into::into(val)
688                })
689        })?;
690
691        // TODO: This should probably emit 'rerun-if-changed=PATH' directives for cargo, however
692        // according to [1] if any are output then those paths replace the default crate root,
693        // which is undesirable. Figure out how to do it in an additive way; perhaps gcc-rs has
694        // this figured out.
695        // [1]: http://doc.crates.io/build-script.html#outputs-of-the-build-script
696
697        let tmp;
698        let file_descriptor_set_path = if let Some(path) = &self.file_descriptor_set_path {
699            path.clone()
700        } else {
701            if self.skip_protoc_run {
702                return Err(Error::other(
703                    "file_descriptor_set_path is required with skip_protoc_run",
704                ));
705            }
706            tmp = tempfile::Builder::new().prefix("prost-build").tempdir()?;
707            tmp.path().join("prost-descriptor-set")
708        };
709
710        if !self.skip_protoc_run {
711            let mut cmd = Command::new(protoc());
712            cmd.arg("--include_imports")
713                .arg("--include_source_info")
714                .arg("-o")
715                .arg(&file_descriptor_set_path);
716
717            for include in includes {
718                cmd.arg("-I").arg(include.as_ref());
719            }
720
721            // Set the protoc include after the user includes in case the user wants to
722            // override one of the built-in .protos.
723            cmd.arg("-I").arg(protoc_include());
724
725            for arg in &self.protoc_args {
726                cmd.arg(arg);
727            }
728
729            for proto in protos {
730                cmd.arg(proto.as_ref());
731            }
732
733            let output = cmd.output().map_err(|error| {
734                Error::new(
735                    error.kind(),
736                    format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): {error}"),
737                )
738            })?;
739
740            if !output.status.success() {
741                return Err(Error::other(format!(
742                    "protoc failed: {}",
743                    String::from_utf8_lossy(&output.stderr)
744                )));
745            }
746        }
747
748        let buf = fs::read(file_descriptor_set_path)?;
749        let file_descriptor_set = FileDescriptorSet::decode(&*buf).map_err(|error| {
750            Error::new(
751                ErrorKind::InvalidInput,
752                format!("invalid FileDescriptorSet: {error}"),
753            )
754        })?;
755
756        let requests = file_descriptor_set
757            .file
758            .into_iter()
759            .map(|descriptor| {
760                (
761                    Module::from_protobuf_package_name(descriptor.package()),
762                    descriptor,
763                )
764            })
765            .collect::<Vec<_>>();
766
767        let file_names = requests
768            .iter()
769            .map(|req| {
770                (
771                    req.0.clone(),
772                    req.0.to_file_name_or(&self.default_package_filename),
773                )
774            })
775            .collect::<HashMap<Module, String>>();
776
777        let modules = self.generate(requests)?;
778        for (module, content) in &modules {
779            let file_name = file_names
780                .get(module)
781                .expect("every module should have a filename");
782            if file_name == "google.protobuf.rs" || file_name == "google_protobuf.rs" {
783                continue;
784            }
785            let output_path = target.join(file_name);
786            let previous_content = fs::read(&output_path);
787
788            if previous_content
789                .map(|previous_content| previous_content == content.as_bytes())
790                .unwrap_or(false)
791            {
792                trace!("unchanged: {file_name:?}");
793            } else {
794                trace!("writing: {file_name:?}");
795                fs::write(output_path, content)?;
796            }
797        }
798
799        if let Some(ref include_file) = self.include_file {
800            trace!("Writing include file: {:?}", target.join(include_file));
801            let mut file = fs::File::create(target.join(include_file))?;
802            self.write_includes(
803                modules.keys().collect(),
804                &mut file,
805                0,
806                if target_is_env { None } else { Some(&target) },
807            )?;
808            file.flush()?;
809        }
810
811        Ok(())
812    }
813
814    fn write_includes(
815        &self,
816        mut entries: Vec<&Module>,
817        outfile: &mut fs::File,
818        depth: usize,
819        basepath: Option<&PathBuf>,
820    ) -> Result<usize> {
821        let mut written = 0;
822        while !entries.is_empty() {
823            let modident = entries[0].part(depth);
824            let matching: Vec<&Module> = entries
825                .iter()
826                .filter(|&v| v.part(depth) == modident)
827                .copied()
828                .collect();
829            {
830                // Will NLL sort this mess out?
831                let _temp = entries
832                    .drain(..)
833                    .filter(|&v| v.part(depth) != modident)
834                    .collect();
835                entries = _temp;
836            }
837            self.write_line(outfile, depth, &format!("pub mod {modident} {{"))?;
838            let subwritten = self.write_includes(
839                matching
840                    .iter()
841                    .filter(|v| v.len() > depth + 1)
842                    .copied()
843                    .collect(),
844                outfile,
845                depth + 1,
846                basepath,
847            )?;
848            written += subwritten;
849            if subwritten != matching.len() {
850                let modname = matching[0].to_partial_file_name(..=depth);
851                if basepath.is_some() {
852                    self.write_line(outfile, depth + 1, &format!("include!(\"{modname}.rs\");"))?;
853                } else {
854                    self.write_line(
855                        outfile,
856                        depth + 1,
857                        &format!("include!(concat!(env!(\"OUT_DIR\"), \"/{modname}.rs\"));"),
858                    )?;
859                }
860                written += 1;
861            }
862
863            self.write_line(outfile, depth, "}")?;
864        }
865        Ok(written)
866    }
867
868    fn write_line(&self, outfile: &mut fs::File, depth: usize, line: &str) -> Result<()> {
869        outfile.write_all(format!("{}{}\n", ("    ").to_owned().repeat(depth), line).as_bytes())
870    }
871
872    /// Processes a set of modules and file descriptors, returning a map of modules to generated
873    /// code contents.
874    ///
875    /// This is generally used when control over the output should not be managed by Prost,
876    /// such as in a flow for a `protoc` code generating plugin. When compiling as part of a
877    /// `build.rs` file, instead use [`compile_protos()`].
878    pub fn generate(
879        &mut self,
880        requests: Vec<(Module, FileDescriptorProto)>,
881    ) -> Result<HashMap<Module, String>> {
882        let mut modules = HashMap::new();
883        let mut packages = HashMap::new();
884
885        let extern_paths = ExternPaths::new(&self.extern_paths, self.prost_types)
886            .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?;
887
888        for request in requests {
889            // Only record packages that have services
890            if !request.1.service.is_empty() {
891                packages.insert(request.0.clone(), request.1.package().to_string());
892            }
893
894            if !modules.contains_key(&request.0) {
895                let mut buf = String::new();
896                buf.insert_str(
897                    0,
898                    "#![allow(dead_code, unused_mut, unused_variables, clippy::identity_op, clippy::too_many_lines, clippy::derivable_impls, clippy::unit_arg, clippy::derive_partial_eq_without_eq, clippy::manual_range_patterns, clippy::default_trait_access, clippy::semicolon_if_nothing_returned, clippy::doc_markdown, clippy::wildcard_imports)]\n//! DO NOT MODIFY. Auto-generated file\n\n",
899                );
900                modules.insert(request.0.clone(), buf);
901            }
902            let buf = modules.get_mut(&request.0).unwrap();
903
904            CodeGenerator::generate(self, &extern_paths, request.1, buf);
905        }
906
907        if let Some(ref mut service_generator) = self.service_generator {
908            for (module, package) in packages {
909                let buf = modules.get_mut(&module).unwrap();
910                service_generator.finalize_package(&package, buf);
911            }
912        }
913
914        Ok(modules)
915    }
916}
917
918impl default::Default for Config {
919    fn default() -> Config {
920        Config {
921            file_descriptor_set_path: None,
922            service_generator: None,
923            types_map: PathMap::default(),
924            type_attributes: PathMap::default(),
925            field_attributes: PathMap::default(),
926            prost_types: true,
927            strip_enum_prefix: true,
928            out_dir: None,
929            extern_paths: Vec::new(),
930            default_package_filename: "_".to_string(),
931            protoc_args: Vec::new(),
932            disable_comments: PathMap::default(),
933            skip_protoc_run: false,
934            include_file: None,
935        }
936    }
937}
938
939impl fmt::Debug for Config {
940    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
941        fmt.debug_struct("Config")
942            .field("file_descriptor_set_path", &self.file_descriptor_set_path)
943            .field("service_generator", &self.service_generator.is_some())
944            .field("types_map", &self.types_map)
945            .field("type_attributes", &self.type_attributes)
946            .field("field_attributes", &self.field_attributes)
947            .field("prost_types", &self.prost_types)
948            .field("strip_enum_prefix", &self.strip_enum_prefix)
949            .field("out_dir", &self.out_dir)
950            .field("extern_paths", &self.extern_paths)
951            .field("default_package_filename", &self.default_package_filename)
952            .field("protoc_args", &self.protoc_args)
953            .field("disable_comments", &self.disable_comments)
954            .finish()
955    }
956}
957
958/// A Rust module path for a Protobuf package.
959#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
960pub struct Module {
961    components: Vec<String>,
962}
963
964impl Module {
965    /// Construct a module path from an iterator of parts.
966    pub fn from_parts<I>(parts: I) -> Self
967    where
968        I: IntoIterator,
969        I::Item: Into<String>,
970    {
971        Self {
972            components: parts.into_iter().map(|s| s.into()).collect(),
973        }
974    }
975
976    /// Construct a module path from a Protobuf package name.
977    ///
978    /// Constituent parts are automatically converted to snake case in order to follow
979    /// Rust module naming conventions.
980    pub fn from_protobuf_package_name(name: &str) -> Self {
981        Self {
982            components: name
983                .split('.')
984                .filter(|s| !s.is_empty())
985                .map(to_snake)
986                .collect(),
987        }
988    }
989
990    /// An iterator over the parts of the path.
991    pub fn parts(&self) -> impl Iterator<Item = &str> {
992        self.components.iter().map(|s| s.as_str())
993    }
994
995    /// Format the module path into a filename for generated Rust code.
996    ///
997    /// If the module path is empty, `default` is used to provide the root of the filename.
998    pub fn to_file_name_or(&self, default: &str) -> String {
999        let mut root = if self.components.is_empty() {
1000            default.to_owned()
1001        } else {
1002            self.components.join(".")
1003        };
1004
1005        if !root.ends_with(".rs") {
1006            root.push_str(".rs");
1007        }
1008        let mut root = root[0..root.len() - 3].replace(".", "_");
1009        root.push_str(".rs");
1010
1011        root
1012    }
1013
1014    /// The number of parts in the module's path.
1015    pub fn len(&self) -> usize {
1016        self.components.len()
1017    }
1018
1019    /// Whether the module's path contains any components.
1020    pub fn is_empty(&self) -> bool {
1021        self.components.is_empty()
1022    }
1023
1024    fn to_partial_file_name(&self, range: RangeToInclusive<usize>) -> String {
1025        self.components[range].join(".")
1026    }
1027
1028    fn part(&self, idx: usize) -> &str {
1029        self.components[idx].as_str()
1030    }
1031}
1032
1033impl fmt::Display for Module {
1034    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1035        let mut parts = self.parts();
1036        if let Some(first) = parts.next() {
1037            f.write_str(first)?;
1038        }
1039        for part in parts {
1040            f.write_str("::")?;
1041            f.write_str(part)?;
1042        }
1043        Ok(())
1044    }
1045}
1046
1047/// Compile `.proto` files into Rust files during a Cargo build.
1048///
1049/// The generated `.rs` files are written to the Cargo `OUT_DIR` directory, suitable for use with
1050/// the [include!][1] macro. See the [Cargo `build.rs` code generation][2] example for more info.
1051///
1052/// This function should be called in a project's `build.rs`.
1053///
1054/// # Arguments
1055///
1056/// **`protos`** - Paths to `.proto` files to compile. Any transitively [imported][3] `.proto`
1057/// files are automatically be included.
1058///
1059/// **`includes`** - Paths to directories in which to search for imports. Directories are searched
1060/// in order. The `.proto` files passed in **`protos`** must be found in one of the provided
1061/// include directories.
1062///
1063/// # Errors
1064///
1065/// This function can fail for a number of reasons:
1066///
1067///   - Failure to locate or download `protoc`.
1068///   - Failure to parse the `.proto`s.
1069///   - Failure to locate an imported `.proto`.
1070///   - Failure to compile a `.proto` without a [package specifier][4].
1071///
1072/// It's expected that this function call be `unwrap`ed in a `build.rs`; there is typically no
1073/// reason to gracefully recover from errors during a build.
1074///
1075/// # Example `build.rs`
1076///
1077/// ```rust,no_run
1078/// # use std::io::Result;
1079/// fn main() -> Result<()> {
1080///   prost_build::compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?;
1081///   Ok(())
1082/// }
1083/// ```
1084///
1085/// [1]: https://doc.rust-lang.org/std/macro.include.html
1086/// [2]: http://doc.crates.io/build-script.html#case-study-code-generation
1087/// [3]: https://developers.google.com/protocol-buffers/docs/proto3#importing-definitions
1088/// [4]: https://developers.google.com/protocol-buffers/docs/proto#packages
1089pub fn compile_protos(protos: &[impl AsRef<Path>], includes: &[impl AsRef<Path>]) -> Result<()> {
1090    Config::new().compile_protos(protos, includes)
1091}
1092
1093/// Returns the path to the `protoc` binary.
1094pub fn protoc() -> PathBuf {
1095    match env::var_os("PROTOC") {
1096        Some(protoc) => PathBuf::from(protoc),
1097        None => PathBuf::from(env!("PROTOC")),
1098    }
1099}
1100
1101/// Returns the path to the Protobuf include directory.
1102pub fn protoc_include() -> PathBuf {
1103    match env::var_os("PROTOC_INCLUDE") {
1104        Some(include) => PathBuf::from(include),
1105        None => PathBuf::from(env!("PROTOC_INCLUDE")),
1106    }
1107}