informalsystems_prost_build/
lib.rs

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