1#[macro_export]
34macro_rules! env_struct {
35 (
36 $(#[$outer:meta])*
37 $vis:vis struct $struct_name:ident {
38 $(
39 $(#[$outer_field:meta])*
40 $vis_ident:vis $field:ident = $fieldDef:expr,
41 )*
42 }
43 ) => {
44 $(#[$outer])*
45 $vis struct $struct_name {
46 $(
47 $(#[$outer_field])*
48 $vis_ident $field: String,
49 )*
50 }
51 impl $struct_name {
52 pub fn load_from_env() -> Self {
53 let mut env = Self::default();
54 $(
55 let _field = stringify!($field)
56 .chars()
57 .map(|x| char::to_ascii_uppercase(&x))
58 .collect::<String>();
59 if let Ok(s) = std::env::var(&_field) {
60 env.$field = s;
61 } else {
62 $crate::log()
63 }
64 )*
65 env
66 }
67 }
68 impl Default for $struct_name {
69 fn default() -> Self {
70 Self {
71 $(
72 $field: $fieldDef,
73 )*
74 }
75 }
76 }
77 };
78 (
79 $(#[$outer:meta])*
80 $vis:vis struct $struct_name:ident {
81 $(
82 $(#[$outer_field:meta])*
83 $vis_ident:vis $field:ident,
84 )*
85 }
86 ) => {
87 $(#[$outer])*
88 $vis struct $struct_name {
89 $(
90 $(#[$outer_field])*
91 $vis_ident $field: String,
92 )*
93 }
94 impl $struct_name {
95 pub fn try_load_from_env() -> Result<Self, String> {
96 Ok(Self {
97 $(
98 $field: std::env::var(
99 stringify!($field)
100 .chars()
101 .map(|x| char::to_ascii_uppercase(&x))
102 .collect::<String>(),
103 ).map_err(|_| {
104 format!(
105 "Environment Variable `{}` Not Present!",
106 stringify!($field)
107 .chars()
108 .map(|x| char::to_ascii_uppercase(&x))
109 .collect::<String>()
110 )
111 })?,
112 )*
113 })
114 }
115 }
116 };
117}
118
119#[cfg(not(feature = "logging"))]
120pub fn log() {}
121
122#[cfg(feature = "logging")]
123pub fn log(def: String) {
124 use log;
125 log::warn!(
126 "Failed to find `{}` in env, defaulting to {:?}",
127 _field,
128 def
129 );
130}
131
132#[cfg(test)]
133mod tests {
134
135 struct EnvTemp {
136 flag: &'static str,
137 original_content: Option<String>,
138 }
139 impl EnvTemp {
140 fn set_var(flag: &'static str, val: &'static str) -> EnvTemp {
141 let env = EnvTemp {
142 flag,
143 original_content: std::env::var(flag).ok(),
144 };
145 std::env::set_var(flag, val);
146 env
147 }
148 }
149 impl Drop for EnvTemp {
150 fn drop(&mut self) {
151 if let Some(og) = &self.original_content {
153 std::env::set_var(self.flag, og);
154 } else {
155 std::env::remove_var(self.flag);
156 }
157 }
158 }
159
160 #[test]
161 fn test_with_default() {
162 let hello_world = "Hello, world!";
163 let temp_env = [EnvTemp::set_var("HELLO_NOT_MY_WORLD", hello_world)];
164 env_struct! {
165 struct Env {
167 hello_not_my_world = "hello".into(),
169 hello_some_world = "Hello, Some World!".into(),
170 }
171 }
172 let env = Env::load_from_env();
173 assert_eq!(env.hello_not_my_world, hello_world);
174 assert_eq!(env.hello_some_world, "Hello, Some World!");
175 drop(temp_env); }
177
178 #[test]
179 fn test_no_defaults_succeed() {
180 let hello_sam = "Hello, Sam!";
181 let welp_sam = "Welp, Sam!";
182 let temp_env = [
183 EnvTemp::set_var("HELLO_WORLD", hello_sam),
184 EnvTemp::set_var("WELP_MY_WORLD", welp_sam),
185 ];
186 env_struct! {
187 struct Env2 {
189 hello_world,
191 welp_my_world,
192 }
193 }
194 let env = Env2::try_load_from_env().unwrap();
195 assert_eq!(env.hello_world, hello_sam);
196 assert_eq!(env.welp_my_world, welp_sam);
197 drop(temp_env); }
199
200 #[test]
201 #[should_panic]
202 fn test_no_defaults_failed() {
203 let welp_sam = "Welp, Sam!";
204 let temp_env = [EnvTemp::set_var("HELL_TO_WORLD", welp_sam)];
205 env_struct! {
206 struct Env {
207 hell_to_world,
208 welp_world,
209 }
210 }
211 let env = Env::try_load_from_env().unwrap();
212 _ = env.hell_to_world;
213 _ = env.welp_world;
214 drop(temp_env); }
216}