shadow_rs/build.rs
1use crate::date_time::DEFINE_SOURCE_DATE_EPOCH;
2use crate::hook::HookExt;
3use crate::shadow::DEFINE_SHADOW_RS;
4use crate::{SdResult, Shadow, CARGO_METADATA};
5use is_debug::is_debug;
6use std::collections::BTreeSet;
7use std::fmt::{Display, Formatter};
8
9/// `shadow-rs` build constant identifiers.
10pub type ShadowConst = &'static str;
11
12/// Since [cargo metadata](https://crates.io/crates/cargo_metadata) details about workspace
13/// membership and resolved dependencies for the current package, storing this data can result in
14/// significantly larger crate sizes. As such, the CARGO_METADATA const is disabled by default.
15///
16/// Should you choose to retain this information, you have the option to customize a deny_const
17/// object and override the `new_deny` method parameters accordingly.
18///
19#[allow(clippy::all, clippy::pedantic, clippy::restriction, clippy::nursery)]
20pub fn default_deny() -> BTreeSet<ShadowConst> {
21 BTreeSet::from([CARGO_METADATA])
22}
23
24/// Serialized values for build constants.
25#[derive(Debug, Clone)]
26pub struct ConstVal {
27 /// User-facing documentation for the build constant.
28 pub desc: String,
29 /// Serialized value of the build constant.
30 pub v: String,
31 /// Type of the build constant.
32 pub t: ConstType,
33}
34
35impl ConstVal {
36 pub fn new<S: Into<String>>(desc: S) -> ConstVal {
37 // Creates a new `ConstVal` with an empty string as its value and `Str` as its type.
38 ConstVal {
39 desc: desc.into(),
40 v: "".to_string(),
41 t: ConstType::Str,
42 }
43 }
44
45 pub fn new_bool<S: Into<String>>(desc: S) -> ConstVal {
46 // Creates a new `ConstVal` with "true" as its value and `Bool` as its type.
47 ConstVal {
48 desc: desc.into(),
49 v: "true".to_string(),
50 t: ConstType::Bool,
51 }
52 }
53
54 pub fn new_slice<S: Into<String>>(desc: S) -> ConstVal {
55 // Creates a new `ConstVal` with an empty string as its value and `Slice` as its type.
56 ConstVal {
57 desc: desc.into(),
58 v: "".to_string(),
59 t: ConstType::Slice,
60 }
61 }
62
63 pub fn new_usize<S: Into<String>>(desc: S) -> ConstVal {
64 // Creates a new `ConstVal` with an empty 0 as its value and `Usize` as its type.
65 ConstVal {
66 desc: desc.into(),
67 v: "0".to_string(),
68 t: ConstType::Usize,
69 }
70 }
71}
72
73/// Supported types of build constants.
74#[derive(Debug, Clone)]
75pub enum ConstType {
76 /// [`&str`](`str`).
77 Str,
78 /// [`bool`].
79 Bool,
80 /// [`&[u8]`].
81 Slice,
82 /// [`usize`].
83 Usize,
84 /// [`i64`].
85 Int,
86}
87
88impl Display for ConstType {
89 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
90 match self {
91 ConstType::Str => write!(f, "&str"),
92 ConstType::Bool => write!(f, "bool"),
93 ConstType::Slice => write!(f, "&[u8]"),
94 ConstType::Usize => write!(f, "usize"),
95 ConstType::Int => write!(f, "i64"),
96 }
97 }
98}
99
100/// The BuildPattern enum defines strategies for triggering package rebuilding.
101///
102/// Default mode is `Lazy`.
103///
104/// * `Lazy`: The lazy mode. In this mode, if the current Rust environment is set to `debug`,
105/// the rebuild package will not run every time the build script is triggered.
106/// If the environment is set to `release`, it behaves the same as the `RealTime` mode.
107/// * `RealTime`: The real-time mode. It will always trigger rebuilding a package upon any change,
108/// regardless of whether the Rust environment is set to `debug` or `release`.
109/// * `Custom`: The custom build mode, an enhanced version of `RealTime` mode, allowing for user-defined conditions
110/// to trigger rebuilding a package.
111///
112#[derive(Debug, Default, Clone)]
113pub enum BuildPattern {
114 #[default]
115 Lazy,
116 RealTime,
117 Custom {
118 /// A list of paths that, if changed, will trigger a rebuild.
119 /// See <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed>
120 if_path_changed: Vec<String>,
121 /// A list of environment variables that, if changed, will trigger a rebuild.
122 /// See <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-env-changed>
123 if_env_changed: Vec<String>,
124 },
125}
126
127impl BuildPattern {
128 /// Determines when Cargo should rerun the build script based on the configured pattern.
129 ///
130 /// # Arguments
131 ///
132 /// * `other_keys` - An iterator over additional keys that should trigger a rebuild if they change.
133 /// * `out_dir` - The output directory where generated files are placed.
134 pub(crate) fn rerun_if<'a>(
135 &self,
136 other_keys: impl Iterator<Item = &'a ShadowConst>,
137 out_dir: &str,
138 ) {
139 match self {
140 BuildPattern::Lazy => {
141 if is_debug() {
142 return;
143 }
144 }
145 BuildPattern::RealTime => {}
146 BuildPattern::Custom {
147 if_path_changed,
148 if_env_changed,
149 } => {
150 if_env_changed
151 .iter()
152 .for_each(|key| println!("cargo:rerun-if-env-changed={key}"));
153 if_path_changed
154 .iter()
155 .for_each(|p| println!("cargo:rerun-if-changed={p}"));
156 }
157 }
158
159 other_keys.for_each(|key| println!("cargo:rerun-if-env-changed={key}"));
160 println!("cargo:rerun-if-env-changed={DEFINE_SOURCE_DATE_EPOCH}");
161 println!("cargo:rerun-if-changed={out_dir}/{DEFINE_SHADOW_RS}");
162 }
163}
164
165/// A builder pattern structure to construct a `Shadow` instance.
166///
167/// This struct allows for configuring various aspects of how shadow-rs will be built into your Rust project.
168/// It provides methods to set up hooks, specify build patterns, define paths, and deny certain build constants.
169///
170/// # Fields
171///
172/// * `hook`: An optional hook that can be used during the build process. Hooks implement the `HookExt` trait.
173/// * `build_pattern`: Determines the strategy for triggering package rebuilds (`Lazy`, `RealTime`, or `Custom`).
174/// * `deny_const`: A set of build constant identifiers that should not be included in the build.
175/// * `src_path`: The source path from which files are read for building.
176/// * `out_path`: The output path where generated files will be placed.
177///
178pub struct ShadowBuilder<'a> {
179 hook: Option<Box<dyn HookExt + 'a>>,
180 build_pattern: BuildPattern,
181 deny_const: BTreeSet<ShadowConst>,
182 src_path: Option<String>,
183 out_path: Option<String>,
184}
185
186impl<'a> ShadowBuilder<'a> {
187 /// Creates a new `ShadowBuilder` with default settings.
188 ///
189 /// Initializes the builder with the following defaults:
190 /// - `hook`: None
191 /// - `build_pattern`: `BuildPattern::Lazy`
192 /// - `deny_const`: Uses the result from `default_deny()`
193 /// - `src_path`: Attempts to get the manifest directory using `CARGO_MANIFEST_DIR` environment variable.
194 /// - `out_path`: Attempts to get the output directory using `OUT_DIR` environment variable.
195 ///
196 /// # Returns
197 ///
198 /// A new instance of `ShadowBuilder`.
199 pub fn builder() -> Self {
200 let default_src_path = std::env::var("CARGO_MANIFEST_DIR").ok();
201 let default_out_path = std::env::var("OUT_DIR").ok();
202 Self {
203 hook: None,
204 build_pattern: BuildPattern::default(),
205 deny_const: default_deny(),
206 src_path: default_src_path,
207 out_path: default_out_path,
208 }
209 }
210
211 /// Sets the build hook for this builder.
212 ///
213 /// # Arguments
214 ///
215 /// * `hook` - An object implementing the `HookExt` trait that defines custom behavior for the build process.
216 ///
217 /// # Returns
218 ///
219 /// A new `ShadowBuilder` instance with the specified hook applied.
220 pub fn hook(mut self, hook: impl HookExt + 'a) -> Self {
221 self.hook = Some(Box::new(hook));
222 self
223 }
224
225 /// Sets the source path for this builder.
226 ///
227 /// # Arguments
228 ///
229 /// * `src_path` - A string reference that specifies the source directory for the build.
230 ///
231 /// # Returns
232 ///
233 /// A new `ShadowBuilder` instance with the specified source path.
234 pub fn src_path<P: AsRef<str>>(mut self, src_path: P) -> Self {
235 self.src_path = Some(src_path.as_ref().to_owned());
236 self
237 }
238
239 /// Sets the output path for this builder.
240 ///
241 /// # Arguments
242 ///
243 /// * `out_path` - A string reference that specifies the output directory for the build.
244 ///
245 /// # Returns
246 ///
247 /// A new `ShadowBuilder` instance with the specified output path.
248 pub fn out_path<P: AsRef<str>>(mut self, out_path: P) -> Self {
249 self.out_path = Some(out_path.as_ref().to_owned());
250 self
251 }
252
253 /// Sets the build pattern for this builder.
254 ///
255 /// # Arguments
256 ///
257 /// * `pattern` - A `BuildPattern` that determines when the package should be rebuilt.
258 ///
259 /// # Returns
260 ///
261 /// A new `ShadowBuilder` instance with the specified build pattern.
262 pub fn build_pattern(mut self, pattern: BuildPattern) -> Self {
263 self.build_pattern = pattern;
264 self
265 }
266
267 /// Sets the denied constants for this builder.
268 ///
269 /// # Arguments
270 ///
271 /// * `deny_const` - A set of `ShadowConst` that should be excluded from the build.
272 ///
273 /// # Returns
274 ///
275 /// A new `ShadowBuilder` instance with the specified denied constants.
276 pub fn deny_const(mut self, deny_const: BTreeSet<ShadowConst>) -> Self {
277 self.deny_const = deny_const;
278 self
279 }
280
281 /// Builds a `Shadow` instance based on the current configuration.
282 ///
283 /// # Returns
284 ///
285 /// A `SdResult<Shadow>` that represents the outcome of the build operation.
286 pub fn build(self) -> SdResult<Shadow> {
287 Shadow::build_inner(self)
288 }
289
290 /// Gets the source path if it has been set.
291 ///
292 /// # Returns
293 ///
294 /// A `SdResult<&String>` containing the source path or an error if the path is missing.
295 pub fn get_src_path(&self) -> SdResult<&String> {
296 let src_path = self.src_path.as_ref().ok_or("missing `src_path`")?;
297 Ok(src_path)
298 }
299
300 /// Gets the output path if it has been set.
301 ///
302 /// # Returns
303 ///
304 /// A `SdResult<&String>` containing the output path or an error if the path is missing.
305 pub fn get_out_path(&self) -> SdResult<&String> {
306 let out_path = self.out_path.as_ref().ok_or("missing `out_path`")?;
307 Ok(out_path)
308 }
309
310 /// Gets the build pattern.
311 ///
312 /// # Returns
313 ///
314 /// A reference to the `BuildPattern` currently configured for this builder.
315 pub fn get_build_pattern(&self) -> &BuildPattern {
316 &self.build_pattern
317 }
318
319 /// Gets the denied constants.
320 ///
321 /// # Returns
322 ///
323 /// A reference to the set of `ShadowConst` that are denied for this build.
324 pub fn get_deny_const(&self) -> &BTreeSet<ShadowConst> {
325 &self.deny_const
326 }
327
328 /// Gets the build hook if it has been set.
329 ///
330 /// # Returns
331 ///
332 /// An option containing a reference to the hook if one is present.
333 pub fn get_hook(&'a self) -> Option<&'a (dyn HookExt + 'a)> {
334 self.hook.as_deref()
335 }
336}