better_config_core/utils/
env.rs

1use std::collections::HashMap;
2use std::env;
3use std::ffi::OsStr;
4use std::str::FromStr;
5
6/// Get the value of the environment variable `key` as `T`.
7/// If the variable is not set or cannot be parsed, it returns `None`.
8/// ## Example
9/// ```rust
10/// use std::env;
11/// use std::str::FromStr;
12/// use better_config_core::utils::env::get_optional;
13///
14/// let value: Option<u32> = get_optional("MY_ENV_VAR");
15/// match value {
16///     Some(v) => println!("Value: {}", v),
17///     None => println!("Variable not set or cannot be parsed"),
18/// }
19/// ```
20pub 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
24/// Get the value of the environment variable `key` as `T`.
25/// If the variable is not set or cannot be parsed, it returns `default`.
26/// ## Example
27/// ```rust
28/// use std::env;
29/// use std::str::FromStr;
30/// use better_config_core::utils::env::get_optional_or;
31///
32/// let value: Option<u32> = get_optional_or("MY_ENV_VAR", 42);
33/// match value {
34///   Some(v) => println!("Value: {}", v),
35///    None => println!("Variable not set or cannot be parsed, using default"),
36/// }
37///
38/// ```
39///
40pub 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
47/// Get the value of the environment variable `key` as `T`.
48/// If the variable is not set or cannot be parsed, it returns the result of `f`.
49/// ## Example
50/// ```rust
51/// use std::env;
52/// use std::str::FromStr;
53/// use better_config_core::utils::env::get_optional_or_else;
54///
55/// let value: Option<u32> = get_optional_or_else("MY_ENV_VAR", || 42);
56/// match value {
57///  Some(v) => println!("Value: {}", v),
58///  None => println!("Variable not set or cannot be parsed, using fallback"),
59/// }
60///
61/// ```
62///
63pub 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
73/// Get the value of the environment variable `key` as `T`.
74/// If the variable is not set or cannot be parsed, it panics with a message.
75/// ## Example
76/// ```rust
77/// use std::env;
78/// use std::str::FromStr;
79/// use better_config_core::utils::env::get;
80///
81/// env::set_var("MY_ENV_VAR", "42");
82/// let value: u32 = get("MY_ENV_VAR");
83/// println!("Value: {}", value);
84///
85/// ```
86pub 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
95/// Get the value of the environment variable `key` as `T`.
96/// If the variable is not set or cannot be parsed, it returns `default`.
97/// ## Example
98/// ```rust
99/// use std::env;
100/// use std::str::FromStr;
101/// use better_config_core::utils::env::get_or;
102///
103/// let value: u32 = get_or("MY_ENV_VAR", 42);
104/// println!("Value: {}", value);
105///
106/// ```
107pub 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
114/// Get the value of the environment variable `key` as `T`.
115/// If the variable is not set or cannot be parsed, it returns the result of `f`.
116/// ## Example
117/// ```rust
118/// use std::env;
119/// use std::str::FromStr;
120/// use better_config_core::utils::env::get_or_else;
121///
122/// let value: u32 = get_or_else("MY_ENV_VAR", || 42);
123/// println!("Value: {}", value);
124///
125/// ```
126pub 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
136/// Get the value of the environment variable `key` as `T` from a provided hashmap.
137/// If the variable is not set or cannot be parsed, it returns `default`.
138/// ## Example
139/// ```rust
140/// use std::collections::HashMap;
141/// use std::env;
142/// use std::ffi::OsStr;
143/// use std::str::FromStr;
144/// use better_config_core::utils::env::get_or_with_hashmap;
145///
146/// let mut hashmap = HashMap::new();
147/// hashmap.insert("MY_ENV_VAR".to_string(), "42".to_string());
148/// let value: u32 = get_or_with_hashmap("MY_ENV_VAR", 0, Some(&hashmap));
149/// println!("Value: {}", value);
150///
151/// ```
152pub 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
172/// Get the value of the environment variable `key` as `T` from a provided hashmap.
173/// If the variable is not set or cannot be parsed, it returns the result of `f`.
174/// ## Example
175/// ```rust
176/// use std::env;
177/// use std::ffi::OsStr;
178/// use std::str::FromStr;
179/// use std::collections::HashMap;
180/// use better_config_core::utils::env::get_or_else_with_hashmap;
181///
182/// let mut hashmap = HashMap::new();
183/// hashmap.insert("MY_ENV_VAR".to_string(), "42".to_string());
184/// let value: u32 = get_or_else_with_hashmap("MY_ENV_VAR", || 0, Some(&hashmap));
185/// println!("Value: {}", value);
186///
187/// ```
188///
189pub 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}