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#[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}