1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use std::collections::HashMap;

use crate::ConfigError;

use super::{ConfigSource, ConfigSourceBuilder};

#[doc(hidden)]
#[derive(Debug)]
#[allow(unreachable_pub)]
pub struct Cargo(HashMap<String, String>);

#[allow(dead_code, unreachable_pub)]
impl Cargo {
    #[doc(hidden)]
    pub fn new() -> Self {
        Self(HashMap::new())
    }

    #[doc(hidden)]
    pub fn set<K: Into<String>, V: Into<String>>(&mut self, k: K, v: V) {
        self.0.insert(k.into(), v.into());
    }
}

impl ConfigSource for Cargo {
    fn name(&self) -> &str {
        "cargo_env"
    }

    fn load(&self, builder: &mut ConfigSourceBuilder<'_>) -> Result<(), ConfigError> {
        for (k, v) in &self.0 {
            builder.set(k.as_str(), v.to_string());
        }
        Ok(())
    }
}

/// Collect all `CARGO_PKG_*` env variables, and `CARGO_BIN_NAME` into configuration.
///
/// Please refer to [set_cargo_env](struct.PredefinedConfigurationBuilder.html#method.set_cargo_env) for usage.
#[macro_export]
macro_rules! init_cargo_env {
    () => {
        fn init_cargo_env() -> $crate::Cargo {
            let mut builder = $crate::Cargo::new();
init_cargo_env!(builder: "CARGO_PKG_NAME");
init_cargo_env!(builder: "CARGO_PKG_VERSION");
init_cargo_env!(builder: "CARGO_PKG_VERSION_MAJOR");
init_cargo_env!(builder: "CARGO_PKG_VERSION_MINOR");
init_cargo_env!(builder: "CARGO_PKG_VERSION_PATCH");
init_cargo_env!(builder:? "CARGO_PKG_VERSION_PRE");
init_cargo_env!(builder:? "CARGO_PKG_AUTHORS");
init_cargo_env!(builder:? "CARGO_PKG_DESCRIPTION");
init_cargo_env!(builder:? "CARGO_PKG_HOMEPAGE");
init_cargo_env!(builder:? "CARGO_PKG_REPOSITORY");
init_cargo_env!(builder:? "CARGO_PKG_LICENSE");
init_cargo_env!(builder:? "CARGO_PKG_LICENSE_FILE");
init_cargo_env!(builder:? "CARGO_BIN_NAME");
            builder
        }

    };

    ($b:ident:? $x:literal) => {
        if let Some(v) = option_env!($x) {
            $b.set(&$x.to_lowercase().replace("_", "."), v);
        }
    };

    ($b:ident: $x:literal) => {
        $b.set(&$x.to_lowercase().replace("_", "."), env!($x));
    };
}

#[cfg(test)]
mod test {

    use crate::test::*;
    use crate::*;

    #[derive(FromConfig)]
    struct CargoPkg {
        name: String,
        version: String,
        description: Option<String>,
    }

    #[derive(FromConfig)]
    struct CargoBin {
        #[allow(dead_code)]
        name: String,
    }

    #[derive(FromConfig)]
    #[config(prefix = "cargo")]
    struct CargoEnv {
        pkg: CargoPkg,
        bin: Option<CargoBin>,
    }

    #[test]
    fn cargo_test() {
        init_cargo_env!();
        let c = init_cargo_env().new_config();
        let cargo = c.get_predefined::<CargoEnv>().unwrap();
        assert_eq!(env!("CARGO_PKG_NAME"), cargo.pkg.name);
        assert_eq!(env!("CARGO_PKG_VERSION"), cargo.pkg.version);
        assert_eq!(
            env!("CARGO_PKG_DESCRIPTION"),
            cargo.pkg.description.unwrap()
        );
        assert_eq!(true, cargo.bin.is_none());
    }
}