better_config_core/utils/
env.rs1use std::collections::HashMap;
2use std::env;
3use std::ffi::OsStr;
4use std::str::FromStr;
5
6pub fn get_optional<K: AsRef<OsStr>, T: FromStr>(key: K) -> Option<T> {
21 env::var(key).ok().and_then(|val| val.parse::<T>().ok())
22}
23
24pub fn get_optional_or<K: AsRef<OsStr>, T: FromStr>(key: K, default: T) -> Option<T> {
41 match env::var(key) {
42 Ok(val) => val.parse::<T>().ok(),
43 Err(_) => Some(default),
44 }
45}
46
47pub fn get_optional_or_else<K: AsRef<OsStr>, T: FromStr, F>(key: K, f: F) -> Option<T>
64where
65 F: FnOnce() -> T,
66{
67 match env::var(key) {
68 Ok(val) => val.parse::<T>().ok(),
69 Err(_) => Some(f()),
70 }
71}
72
73pub fn get<K: AsRef<OsStr>, T: FromStr>(key: K) -> T {
87 get_optional(&key).unwrap_or_else(|| {
88 panic!(
89 "Environment variable '{}' is not set or cannot be parsed",
90 &key.as_ref().to_str().unwrap()
91 )
92 })
93}
94
95pub fn get_or<K: AsRef<OsStr>, T: FromStr>(key: K, default: T) -> T {
108 match env::var(key) {
109 Ok(val) => val.parse::<T>().unwrap_or(default),
110 Err(_) => default,
111 }
112}
113
114pub fn get_or_else<K: AsRef<OsStr>, T: FromStr, F>(key: K, f: F) -> T
127where
128 F: FnOnce() -> T,
129{
130 match env::var(key) {
131 Ok(val) => val.parse::<T>().unwrap_or_else(|_| f()),
132 Err(_) => f(),
133 }
134}
135
136pub fn get_or_with_hashmap<K: AsRef<OsStr>, T, S: ::std::hash::BuildHasher>(
153 key: K,
154 default: T,
155 hashmap: Option<&HashMap<String, String, S>>,
156) -> T
157where
158 T: FromStr,
159{
160 match hashmap {
161 None => get_or(key, default),
162 Some(hashmap) => {
163 if let Some(value) = hashmap.get(key.as_ref().to_str().unwrap()) {
164 value.parse::<T>().unwrap_or(default)
165 } else {
166 default
167 }
168 }
169 }
170}
171
172pub fn get_or_else_with_hashmap<K: AsRef<OsStr>, T: FromStr, F, S: ::std::hash::BuildHasher>(
190 key: K,
191 f: F,
192 hashmap: Option<&HashMap<String, String, S>>,
193) -> T
194where
195 F: FnOnce() -> T,
196{
197 match hashmap {
198 None => get_or_else(key, f),
199 Some(hashmap) => {
200 if let Some(value) = hashmap.get(key.as_ref().to_str().unwrap()) {
201 value.parse::<T>().unwrap_or_else(|_| f())
202 } else {
203 f()
204 }
205 }
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use std::env;
213
214 #[test]
215 fn test_get_existing_var() {
216 env::set_var("TEST_VAR", "42");
217 let result: u32 = get("TEST_VAR");
218 assert_eq!(result, 42);
219 env::remove_var("TEST_VAR");
220 }
221
222 #[test]
223 #[should_panic(expected = "Environment variable 'MISSING_VAR' is not set or cannot be parsed")]
224 fn test_get_missing_var() {
225 let _: u32 = get("MISSING_VAR");
226 }
227
228 #[test]
229 #[should_panic(expected = "cannot be parsed")]
230 fn test_get_invalid_format() {
231 env::set_var("INVALID_VAR", "not_a_number");
232 let _: u32 = get("INVALID_VAR");
233 env::remove_var("INVALID_VAR");
234 }
235
236 #[test]
237 fn test_get_with_different_types() {
238 env::set_var("STR_VAR", "hello");
239 env::set_var("BOOL_VAR", "true");
240 env::set_var("FLOAT_VAR", "1.23");
241
242 let s: String = get("STR_VAR");
243 let b: bool = get("BOOL_VAR");
244 let f: f64 = get("FLOAT_VAR");
245
246 assert_eq!(s, "hello");
247 assert!(b);
248 assert_eq!(f, 1.23);
249
250 env::remove_var("STR_VAR");
251 env::remove_var("BOOL_VAR");
252 env::remove_var("FLOAT_VAR");
253 }
254
255 #[test]
256 fn test_get_optional_existing_var() {
257 env::set_var("GET_EXISTING_VAR", "42");
258 let result: Option<u32> = get_optional("GET_EXISTING_VAR");
259 assert_eq!(result, Some(42));
260 env::remove_var("GET_EXISTING_VAR");
261 }
262
263 #[test]
264 fn test_get_optional_missing_var() {
265 let result: Option<u32> = get_optional("MISSING_VAR");
266 assert_eq!(result, None);
267 }
268
269 #[test]
270 fn test_get_optional_invalid_format() {
271 env::set_var("INVALID_VAR", "not_a_number");
272 let result: Option<u32> = get_optional("INVALID_VAR");
273 assert_eq!(result, None);
274 env::remove_var("INVALID_VAR");
275 }
276
277 #[test]
278 fn test_get_optional_or_existing_var() {
279 env::set_var("EXISTING_VAR", "42");
280 let result: Option<u32> = get_optional_or("EXISTING_VAR", 0);
281 assert_eq!(result, Some(42));
282 env::remove_var("EXISTING_VAR");
283 }
284
285 #[test]
286 fn test_get_optional_or_missing_var() {
287 let result: Option<u32> = get_optional_or("MISSING_VAR", 0);
288 assert_eq!(result, Some(0));
289 }
290
291 #[test]
292 fn test_get_optional_or_else_existing_var() {
293 env::set_var("EXISTING_VAR", "42");
294 let result: Option<u32> = get_optional_or_else("EXISTING_VAR", || 0);
295 assert_eq!(result, Some(42));
296 env::remove_var("EXISTING_VAR");
297 }
298
299 #[test]
300 fn test_get_optional_or_else_missing_var() {
301 let result: Option<u32> = get_optional_or_else("MISSING_VAR", || 0);
302 assert_eq!(result, Some(0));
303 }
304
305 #[test]
306 #[should_panic]
307 fn test_get_optional_or_else_panic() {
308 let result: Option<u32> = get_optional_or_else("PANIC_VAR", || panic!("This should panic"));
309 assert!(result.is_none());
310 env::remove_var("PANIC_VAR");
311 }
312
313 #[test]
314 fn test_get_or_existing_var() {
315 env::set_var("EXISTING_VAR", "42");
316 let result: u32 = get_or("EXISTING_VAR", 0);
317 assert_eq!(result, 42);
318 env::remove_var("EXISTING_VAR");
319 }
320
321 #[test]
322 fn test_get_or_missing_var() {
323 let result: u32 = get_or("MISSING_VAR", 0);
324 assert_eq!(result, 0);
325 }
326
327 #[test]
328 fn test_get_or_else_existing_var() {
329 env::set_var("EXISTING_VAR", "42");
330 let result: u32 = get_or_else("EXISTING_VAR", || 0);
331 assert_eq!(result, 42);
332 env::remove_var("EXISTING_VAR");
333 }
334
335 #[test]
336 fn test_get_or_else_missing_var() {
337 let result: u32 = get_or_else("MISSING_VAR", || 0);
338 assert_eq!(result, 0);
339 }
340
341 #[test]
342 #[should_panic]
343 fn test_get_or_else_panic() {
344 let result: u32 = get_or_else("PANIC_VAR", || panic!("This should panic"));
345 assert_eq!(result, 0);
346 env::remove_var("PANIC_VAR");
347 }
348
349 #[test]
350 fn test_get_with_hashmap_existing_var() {
351 let mut hashmap = HashMap::new();
352 hashmap.insert("EXISTING_VAR".to_string(), "42".to_string());
353 let result: u32 = get_or_with_hashmap("EXISTING_VAR", 0, Some(&hashmap));
354 assert_eq!(result, 42);
355 }
356
357 #[test]
358 fn test_get_with_hashmap_missing_var() {
359 let mut hashmap = HashMap::new();
360 hashmap.insert("EXISTING_VAR".to_string(), "42".to_string());
361 let result: u32 = get_or_with_hashmap("MISSING_VAR", 0, Some(&hashmap));
362 assert_eq!(result, 0);
363 }
364
365 #[test]
366 fn test_get_with_hashmap_none() {
367 let result: u32 = get_or_with_hashmap::<_, _, std::collections::hash_map::RandomState>(
368 "EXISTING_VAR",
369 0,
370 None,
371 );
372 assert_eq!(result, 0);
373 }
374
375 #[test]
376 fn test_get_or_else_with_hashmap_existing_var() {
377 let mut hashmap = HashMap::new();
378 hashmap.insert("EXISTING_VAR".to_string(), "42".to_string());
379 let result: u32 = get_or_else_with_hashmap("EXISTING_VAR", || 0, Some(&hashmap));
380 assert_eq!(result, 42);
381 }
382
383 #[test]
384 fn test_get_or_else_with_hashmap_missing_var() {
385 let mut hashmap = HashMap::new();
386 hashmap.insert("EXISTING_VAR".to_string(), "42".to_string());
387 let result: u32 = get_or_else_with_hashmap("MISSING_VAR", || 0, Some(&hashmap));
388 assert_eq!(result, 0);
389 }
390
391 #[test]
392 fn test_get_or_else_with_hashmap_none() {
393 let result: u32 = get_or_else_with_hashmap::<
394 _,
395 _,
396 _,
397 std::collections::hash_map::RandomState,
398 >("EXISTING_VAR", || 0, None);
399 assert_eq!(result, 0);
400 }
401
402 #[test]
403 #[should_panic]
404 fn test_get_or_else_with_hashmap_panic() {
405 let mut hashmap = HashMap::new();
406 hashmap.insert("PANIC_VAR".to_string(), "42".to_string());
407 let result: u32 = get_or_else_with_hashmap::<
408 _,
409 _,
410 _,
411 std::collections::hash_map::RandomState,
412 >("PANIC_VAR", || panic!("This should panic"), Some(&hashmap));
413 assert_eq!(result, 0);
414 env::remove_var("PANIC_VAR");
415 }
416
417 #[test]
418 fn test_get_with_hashmap_with_different_types() {
419 let mut hashmap = HashMap::new();
420 hashmap.insert("STR_VAR".to_string(), "hello".to_string());
421 hashmap.insert("BOOL_VAR".to_string(), "true".to_string());
422 hashmap.insert("FLOAT_VAR".to_string(), "1.23".to_string());
423 let s: String =
424 get_or_with_hashmap::<_, _, _>("STR_VAR", "default".to_string(), Some(&hashmap));
425 let b: bool = get_or_with_hashmap::<_, _, _>("BOOL_VAR", false, Some(&hashmap));
426 let f: f64 = get_or_with_hashmap::<_, _, _>("FLOAT_VAR", 0.0, Some(&hashmap));
427 assert_eq!(s, "hello");
428 assert!(b);
429 assert_eq!(f, 1.23);
430 }
431}