#[doc(hidden)]
pub mod __private {
use std::{marker::PhantomData, str::FromStr};
pub struct MaybeOption<T>(PhantomData<T>);
impl<T> MaybeOption<T> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<T: FromStr> MaybeOption<Option<T>> {
pub fn generate(self, env_var: &str) -> Option<T> {
match std::env::var(env_var) {
Ok(v) => v.parse().ok(),
Err(e) => match e {
std::env::VarError::NotPresent => None,
std::env::VarError::NotUnicode(_) => None,
},
}
}
}
pub trait GenerateFallback {
type Output;
fn generate(self, env_var: &str) -> Self::Output;
}
impl<T: FromStr> GenerateFallback for MaybeOption<T> {
type Output = T;
fn generate(self, env_var: &str) -> Self::Output {
match std::env::var(env_var) {
Ok(v) => match v.parse() {
Ok(v) => v,
Err(_) => panic!("Error while parsing environment variable: {} (does the value match the struct type?)", env_var),
},
Err(e) => match e {
std::env::VarError::NotPresent => {
panic!("Environment variable not found: {}", env_var)
}
std::env::VarError::NotUnicode(_) => panic!(
"Environment variable contains invalid unicode data: {}",
env_var
),
},
}
}
}
}
#[test]
fn test_api() {
use __private::GenerateFallback;
use __private::MaybeOption;
std::env::set_var("SECRET_KEY", "hunter2");
let as_option = <MaybeOption<Option<String>>>::new().generate("SECRET_KEY");
let as_value = <MaybeOption<String>>::new().generate("SECRET_KEY");
assert_eq!(as_option.unwrap(), String::from("hunter2"));
assert_eq!(as_value, String::from("hunter2"));
std::env::set_var("SECRET_KEY", "0xcafebabe");
std::env::set_var("OPTIONAL_DATA", "128");
let secret_key: String = <MaybeOption<String>>::new().generate("SECRET_KEY");
let optional_data: Option<usize> =
<MaybeOption<Option<usize>>>::new().generate("OPTIONAL_DATA");
assert_eq!(secret_key, String::from("0xcafebabe"));
assert_eq!(optional_data, Some(128));
std::env::remove_var("OPTIONAL_DATA");
let optional_data: Option<usize> =
<MaybeOption<Option<usize>>>::new().generate("OPTIONAL_DATA");
assert_eq!(optional_data, None);
}