pb_rs/
lib.rs

1pub mod errors;
2mod keywords;
3mod parser;
4mod scc;
5pub mod types;
6
7use errors::{Error, Result};
8use std::path::{Path, PathBuf};
9use types::Config;
10
11/// A builder for [Config]
12///
13/// # Example build.rs
14///
15/// ```rust,no_run
16/// use pb_rs::{types::FileDescriptor, ConfigBuilder};
17/// use std::path::{Path, PathBuf};
18/// use walkdir::WalkDir;
19///
20/// fn main() {
21///     let out_dir = std::env::var("OUT_DIR").unwrap();
22///     let out_dir = Path::new(&out_dir).join("protos");
23///
24///     let in_dir = PathBuf::from(::std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("protos");
25///     // Re-run this build.rs if the protos dir changes (i.e. a new file is added)
26///     println!("cargo:rerun-if-changed={}", in_dir.to_str().unwrap());
27///
28///     // Find all *.proto files in the `in_dir` and add them to the list of files
29///     let mut protos = Vec::new();
30///     let proto_ext = Some(Path::new("proto").as_os_str());
31///     for entry in WalkDir::new(&in_dir) {
32///         let path = entry.unwrap().into_path();
33///         if path.extension() == proto_ext {
34///             // Re-run this build.rs if any of the files in the protos dir change
35///             println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
36///             protos.push(path);
37///         }
38///     }
39///
40///     // Delete all old generated files before re-generating new ones
41///     if out_dir.exists() {
42///         std::fs::remove_dir_all(&out_dir).unwrap();
43///     }
44///     std::fs::DirBuilder::new().create(&out_dir).unwrap();
45///     let config_builder = ConfigBuilder::new(&protos, None, Some(&out_dir), &[in_dir]).unwrap();
46///     FileDescriptor::run(&config_builder.build()).unwrap()
47/// }
48/// ```
49#[derive(Debug, Default)]
50pub struct ConfigBuilder {
51    in_files: Vec<PathBuf>,
52    out_file: Option<PathBuf>,
53    include_paths: Vec<PathBuf>,
54    single_module: bool,
55    no_output: bool,
56    error_cycle: bool,
57    headers: bool,
58    dont_use_cow: bool,
59    custom_struct_derive: Vec<String>,
60    custom_repr: Option<String>,
61    owned: bool,
62    nostd: bool,
63    hashbrown: bool,
64    gen_info: bool,
65    add_deprecated_fields: bool,
66}
67
68impl ConfigBuilder {
69    pub fn new<P: AsRef<Path>>(
70        in_files: &[P],
71        output: Option<&P>,
72        output_dir: Option<&P>,
73        include_paths: &[P],
74    ) -> Result<ConfigBuilder> {
75        let in_files = in_files
76            .iter()
77            .map(|f| f.as_ref().into())
78            .collect::<Vec<PathBuf>>();
79        let output = output.map(|f| f.as_ref().into());
80        let output_dir: Option<PathBuf> = output_dir.map(|f| f.as_ref().into());
81        let mut include_paths = include_paths
82            .iter()
83            .map(|f| f.as_ref().into())
84            .collect::<Vec<PathBuf>>();
85
86        if in_files.is_empty() {
87            return Err(Error::NoProto);
88        }
89
90        for f in &in_files {
91            if !f.exists() {
92                return Err(Error::InputFile(format!("{}", f.display())));
93            }
94        }
95
96        let out_file = match (output, output_dir) {
97            (Some(_), None) if in_files.len() > 1 => return Err(Error::OutputMultipleInputs),
98            (Some(output), None) => Some(output),
99            (None, Some(output_dir)) => {
100                if !output_dir.is_dir() {
101                    return Err(Error::OutputDirectory(format!("{}", output_dir.display())));
102                }
103                Some(output_dir)
104            }
105            (Some(_), Some(_)) => return Err(Error::OutputAndOutputDir),
106            (None, None) => None,
107        };
108
109        let default = PathBuf::from(".");
110        if include_paths.is_empty() || !include_paths.contains(&default) {
111            include_paths.push(default);
112        }
113
114        Ok(ConfigBuilder {
115            in_files,
116            out_file,
117            include_paths,
118            headers: true,
119            ..Default::default()
120        })
121    }
122
123    /// Omit generation of modules for each package when there is only one package
124    pub fn single_module(mut self, val: bool) -> Self {
125        self.single_module = val;
126        self
127    }
128
129    /// Show enums and messages in this .proto file, including those imported. No code generated.
130    /// `no_output` should probably only be used by the pb-rs cli.
131    pub fn no_output(mut self, val: bool) -> Self {
132        self.no_output = val;
133        self
134    }
135
136    /// Error out if recursive messages do not have optional fields
137    pub fn error_cycle(mut self, val: bool) -> Self {
138        self.error_cycle = val;
139        self
140    }
141
142    /// Enable module comments and module attributes in generated file (default = true)
143    pub fn headers(mut self, val: bool) -> Self {
144        self.headers = val;
145        self
146    }
147
148    /// Add custom values to `#[derive(...)]` at the beginning of every structure
149    pub fn custom_struct_derive(mut self, val: Vec<String>) -> Self {
150        self.custom_struct_derive = val;
151        self
152    }
153
154    /// Add custom values to `#[repr(...)]` at the beginning of every structure
155    pub fn custom_repr(mut self, val: Option<String>) -> Self {
156        self.custom_repr = val;
157        self
158    }
159
160    /// Use `Cow<_,_>` for Strings and Bytes
161    pub fn dont_use_cow(mut self, val: bool) -> Self {
162        self.dont_use_cow = val;
163        self
164    }
165
166    /// Generate Owned structs when the proto struct has a lifetime
167    pub fn owned(mut self, val: bool) -> Self {
168        self.owned = val;
169        self
170    }
171
172    /// Generate `#![no_std]` compliant code
173    pub fn nostd(mut self, val: bool) -> Self {
174        self.nostd = val;
175        self
176    }
177
178    /// Use hashbrown as `HashMap` implementation instead of [std::collections::HashMap] or
179    /// [alloc::collections::BTreeMap](https://doc.rust-lang.org/alloc/collections/btree_map/struct.BTreeMap.html)
180    /// in a `no_std` environment
181    pub fn hashbrown(mut self, val: bool) -> Self {
182        self.hashbrown = val;
183        self
184    }
185
186    /// Generate `MessageInfo` implementations
187    pub fn gen_info(mut self, val: bool) -> Self {
188        self.gen_info = val;
189        self
190    }
191
192    /// Add deprecated fields and mark them as `#[deprecated]`
193    pub fn add_deprecated_fields(mut self, val: bool) -> Self {
194        self.add_deprecated_fields = val;
195        self
196    }
197
198    /// Build [Config] from this `ConfigBuilder`
199    pub fn build(self) -> Vec<Config> {
200        self.in_files
201            .iter()
202            .map(|in_file| {
203                let mut out_file = in_file.with_extension("rs");
204
205                if let Some(ref ofile) = self.out_file {
206                    if ofile.is_dir() {
207                        out_file = ofile.join(out_file.file_name().unwrap());
208                    } else {
209                        out_file = ofile.into();
210                    }
211                }
212
213                Config {
214                    in_file: in_file.to_owned(),
215                    out_file,
216                    import_search_path: self.include_paths.clone(),
217                    single_module: self.single_module,
218                    no_output: self.no_output,
219                    error_cycle: self.error_cycle,
220                    headers: self.headers,
221                    dont_use_cow: self.dont_use_cow, //Change this to true to not use cow with ./generate.sh for v2 and v3 tests
222                    custom_struct_derive: self.custom_struct_derive.clone(),
223                    custom_repr: self.custom_repr.clone(),
224                    custom_rpc_generator: Box::new(|_, _| Ok(())),
225                    custom_includes: Vec::new(),
226                    owned: self.owned,
227                    nostd: self.nostd,
228                    hashbrown: self.hashbrown,
229                    gen_info: self.gen_info,
230                    add_deprecated_fields: self.add_deprecated_fields,
231                }
232            })
233            .collect()
234    }
235}