cfg_rs/source/
cargo.rs

1use std::collections::HashMap;
2
3use crate::{macros::impl_default, ConfigError};
4
5use super::{ConfigSource, ConfigSourceBuilder};
6
7#[doc(hidden)]
8#[derive(Debug)]
9#[allow(unreachable_pub)]
10pub struct Cargo(HashMap<String, String>);
11
12impl_default!(Cargo);
13
14#[allow(dead_code, unreachable_pub)]
15impl Cargo {
16    #[doc(hidden)]
17    pub fn new() -> Self {
18        Self(HashMap::new())
19    }
20
21    #[doc(hidden)]
22    pub fn set<K: Into<String>, V: Into<String>>(&mut self, k: K, v: V) {
23        self.0.insert(k.into(), v.into());
24    }
25}
26
27impl ConfigSource for Cargo {
28    fn name(&self) -> &str {
29        "cargo_env"
30    }
31
32    fn load(&self, builder: &mut ConfigSourceBuilder<'_>) -> Result<(), ConfigError> {
33        for (k, v) in &self.0 {
34            builder.set(k.as_str(), v.to_string());
35        }
36        Ok(())
37    }
38}
39
40/// Collect all `CARGO_PKG_*` env variables, and `CARGO_BIN_NAME` into configuration.
41///
42/// Please refer to [set_cargo_env](struct.PredefinedConfigurationBuilder.html#method.set_cargo_env) for usage.
43#[macro_export]
44macro_rules! init_cargo_env {
45    () => {
46        fn init_cargo_env() -> $crate::Cargo {
47            let mut builder = $crate::Cargo::new();
48init_cargo_env!(builder: "CARGO_PKG_NAME");
49init_cargo_env!(builder: "CARGO_PKG_VERSION");
50init_cargo_env!(builder: "CARGO_PKG_VERSION_MAJOR");
51init_cargo_env!(builder: "CARGO_PKG_VERSION_MINOR");
52init_cargo_env!(builder: "CARGO_PKG_VERSION_PATCH");
53init_cargo_env!(builder:? "CARGO_PKG_VERSION_PRE");
54init_cargo_env!(builder:? "CARGO_PKG_AUTHORS");
55init_cargo_env!(builder:? "CARGO_PKG_DESCRIPTION");
56init_cargo_env!(builder:? "CARGO_PKG_HOMEPAGE");
57init_cargo_env!(builder:? "CARGO_PKG_REPOSITORY");
58init_cargo_env!(builder:? "CARGO_PKG_LICENSE");
59init_cargo_env!(builder:? "CARGO_PKG_LICENSE_FILE");
60init_cargo_env!(builder:? "CARGO_BIN_NAME");
61            builder
62        }
63
64    };
65
66    ($b:ident:? $x:literal) => {
67        if let Some(v) = option_env!($x) {
68            $b.set(&$x.to_lowercase().replace("_", "."), v);
69        }
70    };
71
72    ($b:ident: $x:literal) => {
73        $b.set(&$x.to_lowercase().replace("_", "."), env!($x));
74    };
75}
76
77#[cfg_attr(coverage_nightly, coverage(off))]
78#[cfg(test)]
79mod test {
80
81    use crate::test::*;
82    use crate::*;
83
84    #[derive(FromConfig)]
85    #[config(crate = "crate")]
86    struct CargoPkg {
87        name: String,
88        version: String,
89        description: Option<String>,
90    }
91
92    #[derive(FromConfig)]
93    #[config(crate = "crate")]
94    struct CargoBin {
95        #[allow(dead_code)]
96        name: String,
97    }
98
99    #[derive(FromConfig)]
100    #[config(prefix = "cargo", crate = "crate")]
101    struct CargoEnv {
102        pkg: CargoPkg,
103        bin: Option<CargoBin>,
104    }
105
106    #[test]
107    fn cargo_test() {
108        init_cargo_env!();
109        let c = init_cargo_env().new_config();
110        let cargo = c.get_predefined::<CargoEnv>().unwrap();
111        assert_eq!(env!("CARGO_PKG_NAME"), cargo.pkg.name);
112        assert_eq!(env!("CARGO_PKG_VERSION"), cargo.pkg.version);
113        assert_eq!(
114            env!("CARGO_PKG_DESCRIPTION"),
115            cargo.pkg.description.unwrap()
116        );
117        assert!(cargo.bin.is_none());
118    }
119}