vergen_lib/
entries.rs

1use crate::VergenKey;
2
3use anyhow::{Error, Result};
4use std::collections::BTreeMap;
5
6/// The map used to emit `cargo:rustc-env=NAME=VALUE` cargo instructions
7pub type CargoRustcEnvMap = BTreeMap<VergenKey, String>;
8/// The vector of strings used to emit `cargo:rerun-if-changed=VALUE` cargo instructions
9pub type CargoRerunIfChanged = Vec<String>;
10/// The vector of strings used to emit `cargo:warning=VALUE` cargo instructions
11pub type CargoWarning = Vec<String>;
12
13/// The default configuration to use when an issue has occured generating instructions
14#[derive(Debug)]
15pub struct DefaultConfig {
16    /// Should idempotent output be generated?
17    idempotent: bool,
18    /// Should we fail if an error occurs or output idempotent values on error?
19    fail_on_error: bool,
20    /// The error that caused us to try default instruction output.
21    error: Error,
22}
23
24impl DefaultConfig {
25    /// Create a new [`DefaultConfig`] struct with the given values.
26    #[must_use]
27    pub fn new(idempotent: bool, fail_on_error: bool, error: Error) -> Self {
28        Self {
29            idempotent,
30            fail_on_error,
31            error,
32        }
33    }
34    /// Should idempotent output be generated?
35    #[must_use]
36    pub fn idempotent(&self) -> &bool {
37        &self.idempotent
38    }
39    /// Should we fail if an error occurs or output idempotent values on error?
40    #[must_use]
41    pub fn fail_on_error(&self) -> &bool {
42        &self.fail_on_error
43    }
44    /// The error that caused us to try default instruction output.
45    #[must_use]
46    pub fn error(&self) -> &Error {
47        &self.error
48    }
49}
50
51/// This trait should be implemented to allow the `vergen` emitter
52/// to properly emit instructions for your feature.
53pub trait Add {
54    /// Try to add instructions entries to the various given arguments.
55    ///
56    /// * Write to the `cargo_rustc_env` map to emit 'cargo:rustc-env=NAME=VALUE' instructions.
57    /// * Write to the `cargo_rerun_if_changed` vector to emit 'cargo:rerun-if-changed=VALUE' instructions.
58    /// * Write to the `cargo_warning` vector to emit 'cargo:warning=VALUE' instructions.
59    ///
60    /// # Errors
61    ///
62    /// If an error occurs, the `vergen` emitter will use `add_default_entries` to generate output.
63    /// This assumes generating instructions may fail in some manner so a [`anyhow::Result`] is returned.
64    ///
65    fn add_map_entries(
66        &self,
67        idempotent: bool,
68        cargo_rustc_env: &mut CargoRustcEnvMap,
69        cargo_rerun_if_changed: &mut CargoRerunIfChanged,
70        cargo_warning: &mut CargoWarning,
71    ) -> Result<()>;
72
73    /// Based on the given configuration, emit either default idempotent output or generate a failue.
74    ///
75    /// * Write to the `cargo_rustc_env` map to emit 'cargo:rustc-env=NAME=VALUE' instructions.
76    /// * Write to the `cargo_rerun_if_changed` vector to emit 'cargo:rerun-if-changed=VALUE' instructions.
77    /// * Write to the `cargo_warning` vector to emit 'cargo:warning=VALUE' instructions.
78    ///
79    /// # Errors
80    ///
81    /// This assumes generating instructions may fail in some manner so a [`anyhow::Result`] is returned.
82    ///
83    fn add_default_entries(
84        &self,
85        config: &DefaultConfig,
86        cargo_rustc_env_map: &mut CargoRustcEnvMap,
87        cargo_rerun_if_changed: &mut CargoRerunIfChanged,
88        cargo_warning: &mut CargoWarning,
89    ) -> Result<()>;
90}
91
92/// This trait should be implemented to allow the `vergen` emitter to properly emit your custom instructions.
93///
94/// # Example
95/// ```
96/// # use anyhow::Result;
97/// # use std::collections::BTreeMap;
98/// # use vergen_lib::{AddCustomEntries, CargoRerunIfChanged, CargoWarning, DefaultConfig};
99/// #[derive(Default)]
100/// struct Custom {}
101///
102/// impl AddCustomEntries<&str, &str> for Custom {
103///     fn add_calculated_entries(
104///         &self,
105///         _idempotent: bool,
106///         cargo_rustc_env_map: &mut BTreeMap<&str, &str>,
107///         _cargo_rerun_if_changed: &mut CargoRerunIfChanged,
108///         cargo_warning: &mut CargoWarning,
109///     ) -> Result<()> {
110///         cargo_rustc_env_map.insert("vergen-cl", "custom_instruction");
111///         cargo_warning.push("custom instruction generated".to_string());
112///         Ok(())
113///     }
114///
115///     fn add_default_entries(
116///         &self,
117///         _config: &DefaultConfig,
118///         _cargo_rustc_env_map: &mut BTreeMap<&str, &str>,
119///         _cargo_rerun_if_changed: &mut CargoRerunIfChanged,
120///         _cargo_warning: &mut CargoWarning,
121///     ) -> Result<()> {
122///         Ok(())
123///     }
124/// }
125/// ```
126/// ## Then in [`build.rs`]
127///
128/// ```will_not_compile
129/// let build = BuildBuilder::all_build()?;
130/// let cargo = CargoBuilder::all_cargo()?;
131/// let gix = GixBuilder::all_git()?;
132/// let rustc = RustcBuilder::all_rustc()?;
133/// let si = SysinfoBuilder::all_sysinfo()?;
134/// Emitter::default()
135///     .add_instructions(&build)?
136///     .add_instructions(&cargo)?
137///     .add_instructions(&gix)?
138///     .add_instructions(&rustc)?
139///     .add_instructions(&si)?
140///     .add_custom_instructions(&Custom::default())?
141///     .emit()
142/// ```
143pub trait AddCustom<K: Into<String> + Ord, V: Into<String>> {
144    /// Try to add instructions entries to the various given arguments.
145    ///
146    /// * Write to the `cargo_rustc_env` map to emit 'cargo:rustc-env=NAME=VALUE' instructions.
147    /// * Write to the `cargo_rerun_if_changed` vector to emit 'cargo:rerun-if-changed=VALUE' instructions.
148    /// * Write to the `cargo_warning` vector to emit 'cargo:warning=VALUE' instructions.
149    ///
150    /// # Errors
151    ///
152    /// If an error occurs, the `vergen` emitter will use `add_default_entries` to generate output.
153    /// This assumes generating instructions may fail in some manner so a [`anyhow::Result`] is returned.
154    ///
155    fn add_calculated_entries(
156        &self,
157        idempotent: bool,
158        cargo_rustc_env_map: &mut BTreeMap<K, V>,
159        cargo_rerun_if_changed: &mut CargoRerunIfChanged,
160        cargo_warning: &mut CargoWarning,
161    ) -> Result<()>;
162
163    /// Based on the given configuration, emit either default idempotent output or generate a failue.
164    ///
165    /// * Write to the `cargo_rustc_env` map to emit 'cargo:rustc-env=NAME=VALUE' instructions.
166    /// * Write to the `cargo_rerun_if_changed` vector to emit 'cargo:rerun-if-changed=VALUE' instructions.
167    /// * Write to the `cargo_warning` vector to emit 'cargo:warning=VALUE' instructions.
168    ///
169    /// # Errors
170    ///
171    /// This assumes generating instructions may fail in some manner so a [`anyhow::Result`] is returned.
172    ///
173    fn add_default_entries(
174        &self,
175        config: &DefaultConfig,
176        cargo_rustc_env_map: &mut BTreeMap<K, V>,
177        cargo_rerun_if_changed: &mut CargoRerunIfChanged,
178        cargo_warning: &mut CargoWarning,
179    ) -> Result<()>;
180}
181
182#[doc(hidden)]
183pub(crate) mod test_gen {
184    use crate::{AddCustomEntries, CargoRerunIfChanged, CargoWarning};
185    use anyhow::{Result, anyhow};
186    use bon::Builder;
187    use std::collections::BTreeMap;
188
189    #[doc(hidden)]
190    #[derive(Builder, Clone, Copy, Debug, Default)]
191    pub struct CustomInsGen {
192        #[builder(default = false)]
193        fail: bool,
194    }
195
196    impl AddCustomEntries<&str, &str> for CustomInsGen {
197        fn add_calculated_entries(
198            &self,
199            idempotent: bool,
200            cargo_rustc_env_map: &mut BTreeMap<&str, &str>,
201            _cargo_rerun_if_changed: &mut CargoRerunIfChanged,
202            _cargo_warning: &mut CargoWarning,
203        ) -> Result<()> {
204            if self.fail {
205                Err(anyhow!("We have failed"))
206            } else {
207                if idempotent {
208                    let _ = cargo_rustc_env_map.insert("test", "VERGEN_IDEMPOTENT_OUTPUT");
209                } else {
210                    let _ = cargo_rustc_env_map.insert("test", "value");
211                }
212                Ok(())
213            }
214        }
215
216        fn add_default_entries(
217            &self,
218            config: &crate::DefaultConfig,
219            cargo_rustc_env_map: &mut BTreeMap<&str, &str>,
220            _cargo_rerun_if_changed: &mut CargoRerunIfChanged,
221            _cargo_warning: &mut CargoWarning,
222        ) -> Result<()> {
223            if *config.fail_on_error() {
224                let error = anyhow!(format!("{}", config.error()));
225                Err(error)
226            } else {
227                let _ = cargo_rustc_env_map.insert("test", "VERGEN_IDEMPOTENT_OUTPUT");
228                Ok(())
229            }
230        }
231    }
232}
233
234#[cfg(test)]
235mod test {
236    use super::DefaultConfig;
237    use anyhow::{Result, anyhow};
238    use std::io::Write;
239
240    #[test]
241    fn default_config_debug() -> Result<()> {
242        let config = DefaultConfig::new(true, true, anyhow!("blah"));
243        let mut buf = vec![];
244        write!(buf, "{config:?}")?;
245        assert!(!buf.is_empty());
246        Ok(())
247    }
248}