use crate::error::Error; use estring::EString; use std::convert::TryFrom; pub fn get_or_set_default(env_name: &str, default: R) -> Result where R: TryFrom + std::fmt::Display, { get::(env_name).or_else(|err| match err { Error::NotPresent => sset(env_name, &default).parse().map_err(Error::from), _ => Err(err), }) } pub fn get(env_name: &str) -> Result where R: TryFrom, { sget(env_name).and_then(|v| v.parse().map_err(Error::from)) } pub fn sget(env_name: &str) -> Result { std::env::var(env_name) .map_err(Error::from) .map(EString::from) } pub fn sset(env_name: &str, value: V) -> EString where V: std::fmt::Display, { let val = value.to_string(); std::env::set_var(env_name, &val); val.into() } #[cfg(test)] mod tests { use super::*; struct TestCase; impl std::fmt::Display for TestCase { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "test_case_{}", N) } } #[test] fn should_add_env_variable_to_process() { let en = TestCase::<0>.to_string(); sset(&en, "hello"); match std::env::var(&en) { Ok(var) => assert_eq!(&var, "hello"), _ => unreachable!(), } } #[test] fn should_return_variable() { let en = TestCase::<1>.to_string(); std::env::set_var(&en, "hello"); match get::<&str>(&en) { Ok(res) => assert_eq!(res, "hello"), _ => unreachable!(), }; } #[test] fn should_throw_no_present_error() { let en = TestCase::<2>.to_string(); match get::<&str>(&en) { Err(Error::NotPresent) => {} _ => unreachable!(), }; } #[test] fn should_set_default_if_var_is_no_present() { let en = TestCase::<3>.to_string(); let orig = "hello"; match get_or_set_default(&en, orig) { Ok(res) => { assert_eq!(res, orig); assert_eq!(std::env::var(&en).unwrap(), orig); } _ => unreachable!(), }; } #[cfg(feature = "number")] mod numbers { use super::*; #[test] fn should_return_parsed_num() { let en = TestCase::<4>.to_string(); std::env::set_var(&en, "-10"); match get::(&en) { Ok(res) => assert_eq!(res, -10), _ => unreachable!(), }; } #[test] fn should_throw_parse_error() { let en = TestCase::<5>.to_string(); std::env::set_var(&en, "-10"); match get::(&en) { Err(Error::Parse(orig)) => { assert_eq!(orig, String::from("-10")) } _ => unreachable!(), }; } #[test] fn should_set_default_num_if_var_is_no_present() { let en = TestCase::<6>.to_string(); let orig = 10; match get_or_set_default(&en, orig) { Ok(res) => { assert_eq!(res, orig); assert_eq!(std::env::var(&en).unwrap(), "10"); } _ => unreachable!(), }; } } #[cfg(feature = "bool")] mod boolean { use super::*; #[test] fn should_parse_bool_variable() { let en = TestCase::<7>.to_string(); [ ("1", true), ("y", true), ("yes", true), ("true", true), ("t", true), ("on", true), ("false", false), ("f", false), ("0", false), ] .iter() .for_each(|(val, expected)| { let mut en = en.clone(); en.push_str(val.as_ref()); std::env::set_var(&en, val); match get::(&en) { Ok(res) => assert_eq!(res, *expected), _ => unreachable!(), }; }) } } #[cfg(feature = "vec")] mod vector { use super::*; use crate::estr::{CommaVec, SemiVec, SepVec}; #[test] fn should_return_var_as_vector() { let en = TestCase::<8>.to_string(); std::env::set_var(&en, "1,2,3,4,5"); match get::>(&en) { Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), _ => unreachable!(), }; } #[test] fn should_trim_identations_before_parsing() { let en = TestCase::<9>.to_string(); let input = " 1 , 2, 3, 4,5"; std::env::set_var(&en, input); match get::>(&en) { Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), _ => unreachable!(), }; } #[test] fn should_return_vector_of_vectors() { let en = TestCase::<10>.to_string(); std::env::set_var(&en, "1,2; 3,4,5; 6,7"); match get::>>(&en) { Ok(res) => assert_eq!( res, SemiVec::from(vec![ CommaVec::from(vec![1, 2]), CommaVec::from(vec![3, 4, 5]), CommaVec::from(vec![6, 7]) ]) ), _ => unreachable!(), }; } #[test] fn should_throw_parse_vec_error() { let en = TestCase::<11>.to_string(); std::env::set_var(&en, "1,2,3,4,5"); match get::>(&en) { Err(Error::Parse(orig)) => { assert_eq!(orig, String::from("1,2,3,4,5")) } _ => unreachable!(), }; } #[test] fn should_set_default_vector_if_var_is_no_present() { let en = TestCase::<12>.to_string(); let orig = CommaVec::from(vec![1, 2, 3, 4]); match get_or_set_default(&en, orig.clone()) { Ok(res) => { assert_eq!(res, orig); assert_eq!(std::env::var(&en).unwrap(), "1,2,3,4"); } _ => unreachable!(), }; } } }