use std::{collections::HashMap, str::FromStr};
pub fn parse_from_string<T: std::str::FromStr>(value: String) -> Result<T, String>
where
<T as FromStr>::Err: std::fmt::Debug,
{
Ok(value.trim().parse::<T>().expect("Failed to parse!!"))
}
pub fn get_env_var_value(key: &str) -> Result<String, String> {
Ok(std::env::var(key).unwrap_or_else(|_| panic!("{} environment variable is not defined", key)))
}
pub fn get_env_vars_with_prefix(prefix: &str) -> Option<HashMap<String, String>> {
let matching_props = std::env::vars().filter(|x| x.0.contains(prefix)).fold(HashMap::new(), |mut acc, x| {
acc.insert(x.0, x.1);
acc
});
Some(matching_props)
}
#[macro_export]
macro_rules! env_var {
($key: expr) => {
std::env::var($key).unwrap_or_else(|_| panic!("{} environment variable is not defined", $key))
};
($key: expr, Vec<$type: ty>) => {{
let value_string = env_var!($key);
let value_vec = value_string.split(',').map(|v| {
v.trim()
.parse::<$type>()
.unwrap_or_else(|_| panic!("error parsing \"{value_string}\" String -> Vec<{}>", stringify!($type)))
});
value_vec.collect::<Vec<$type>>()
}};
($key: expr, $type: ty) => {{
let value_string = env_var!($key);
value_string
.parse::<$type>()
.unwrap_or_else(|_| panic!("error parsing \"{value_string}\" String -> {}", stringify!($type)))
}};
}
#[macro_export]
macro_rules! env_var_with_defaults {
($key: expr, Option::<$type: ty>, $default: expr ) => {{
let default_value: $type = $default;
let value_string = std::env::var($key);
let res = if let Ok(v) = value_string {
Some(v.parse::<$type>().unwrap())
} else {
Some(default_value)
};
res
}};
($key: expr, Option::<$type: ty>) => {{
let value_string = std::env::var($key);
let res = if let Ok(v) = value_string { Some(v.parse::<$type>().unwrap()) } else { None };
res
}};
($key: expr, $default: expr) => {
std::env::var($key).unwrap_or_else(|_| $default)
};
($key: expr, $type: ty, $default: expr ) => {{
let value_string = std::env::var($key);
let res = if let Ok(v) = value_string { v.parse::<$type>().unwrap() } else { $default };
res
}};
}
#[cfg(test)]
mod tests {
use std::env;
use serial_test::serial;
fn set_env_var(key: &str, value: &str) {
env::set_var(key, value)
}
fn unset_env_var(key: &str) {
env::remove_var(key)
}
#[test]
#[serial]
fn test_env_var_macro_get_value_successfully_for_key() {
set_env_var("keyA", "valueA");
let val = env_var!("keyA");
assert_eq!(val, "valueA".to_owned());
set_env_var("keyA", "true");
let val = env_var!("keyA", bool);
assert!(val);
set_env_var("keyA", "12, 20, 33");
let val = env_var!("keyA", Vec<u32>);
assert_eq!(val.len(), 3);
let first_val = *val.first().unwrap();
assert_eq!(first_val, 12);
unset_env_var("keyA");
}
#[test]
#[serial]
#[should_panic(expected = "keyB environment variable is not defined")]
fn test_env_var_macro_when_key_value_not_found() {
set_env_var("keyAE1", "valueA");
let _val = env_var!("keyB");
unset_env_var("keyAE1");
}
#[test]
#[serial]
#[should_panic(expected = "error parsing \"valueA\" String -> u32")]
fn test_env_var_macro_when_parsing_fails() {
set_env_var("keyAE2", "valueA");
let _val = env_var!("keyAE2", u32);
unset_env_var("keyAE2");
}
#[test]
#[serial]
#[should_panic(expected = "error parsing \"1, 2 ,valueA\" String -> Vec<u32>")]
fn test_env_var_macro_when_parsing_fails_for_vector() {
set_env_var("keyAE3", "1, 2 ,valueA");
let _val = env_var!("keyAE3", Vec<u32>);
unset_env_var("keyAE3");
}
#[test]
#[serial]
fn test_env_var_with_default_macro_get_value_successfully_for_key() {
let val = env_var_with_defaults!("keyA", "test_string".to_owned());
assert_eq!(val, "test_string".to_owned());
set_env_var("keyA", "30");
let val = env_var_with_defaults!("keyA", Option::<u64>, 10);
assert_eq!(val, Some(30));
unset_env_var("keyA");
let val = env_var_with_defaults!("keyA", Option::<u64>);
assert!(val.is_none());
set_env_var("keyA", "30");
let val = env_var_with_defaults!("keyA", u64, 10);
assert_eq!(val, 30);
unset_env_var("keyA");
unset_env_var("keyA");
}
}