use std::ops::Deref; #[doc(hidden)] pub trait ToEnvString { fn to_env_string(&self) -> EnvString; } #[doc(hidden)] pub trait FromEnvString: Sized { type Err; fn from_env_string(s: &EnvString) -> Result; } impl ToEnvString for T where T: ToString, { #[inline] fn to_env_string(&self) -> EnvString { EnvString(self.to_string()) } } #[doc(hidden)] macro_rules! from_env_string_numbers_impl { ($($ty:ty => $feature:expr),+) => { $( #[cfg(feature = $feature)] impl FromEnvString for $ty { type Err = <$ty as std::str::FromStr>::Err; #[inline] fn from_env_string(s: &EnvString) -> Result { s.0.parse::() } } )+ }; } from_env_string_numbers_impl![ i8 => "i8", i16 => "i16", i32 => "i32", i64 => "i64", i128 => "i128", isize => "isize", u8 => "u8", u16 => "u16", u32 => "u32", u64 => "u64", u128 => "u128", usize => "usize", f32 => "f32", f64 => "f64" ]; #[cfg(feature = "bool")] impl FromEnvString for bool { type Err = (); fn from_env_string(s: &EnvString) -> Result { match s.to_lowercase().as_str() { "true" | "t" | "yes" | "y" | "on" | "1" => Ok(true), _ => Ok(false), } } } #[cfg(feature = "array")] #[derive(Debug)] pub enum ArrayEnvError { InvalidType, FailedToParse, } #[cfg(feature = "array")] impl FromEnvString for Vec where T: FromEnvString, { type Err = ArrayEnvError; fn from_env_string(s: &EnvString) -> Result { serde_json::from_str::>(s.trim()) .map(|vec| vec.iter().map(|v| v.to_string()).collect::>()) .or_else(|_| serde_json::from_str::>(s.trim())) .map_err(|_| ArrayEnvError::InvalidType) .and_then(|vec| { vec.iter() .map(|v| { v.to_env_string() .parse::() .map_err(|_| ArrayEnvError::FailedToParse) }) .collect::, _>>() }) } } impl FromEnvString for String { type Err = (); fn from_env_string(s: &EnvString) -> Result { Ok(s.0.clone()) } } impl FromEnvString for &'static str { type Err = (); fn from_env_string(s: &EnvString) -> Result { Ok(Box::leak(s.0.clone().into_boxed_str())) } } #[doc(hidden)] #[derive(Debug, PartialEq, Clone)] pub struct EnvString(String); impl EnvString { pub fn parse(&self) -> Result { FromEnvString::from_env_string(self) } } impl Deref for EnvString { type Target = String; fn deref(&self) -> &Self::Target { &self.0 } }