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