use std::str::FromStr;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::EnumIs, Default, Hash)]
#[strum(serialize_all = "PascalCase")]
pub enum EnvType {
#[default]
#[strum(
serialize = "develop",
serialize = "Develop",
serialize = "dev",
serialize = "Dev",
serialize = "DEV",
serialize = "d",
serialize = "D"
)]
Dev,
#[strum(
serialize = "test",
serialize = "Test",
serialize = "TEST",
serialize = "t",
serialize = "T"
)]
Test,
#[strum(
serialize = "staging",
serialize = "Staging",
serialize = "stg",
serialize = "Stg",
serialize = "STG",
serialize = "s",
serialize = "S"
)]
Stg,
#[strum(
serialize = "production",
serialize = "Production",
serialize = "prod",
serialize = "Prod",
serialize = "PROD",
serialize = "p",
serialize = "P"
)]
Prod,
Custom(&'static str),
}
#[derive(Debug, Error)]
pub enum EnvError {
#[error("No current environment specified")]
NoCurrentEnv,
#[error("Context not found for type")]
ContextNotFound,
#[error("Context value not found for env")]
ContextValueNotFound,
#[error("Invalid configuration: {0}")]
InvalidConfig(String),
#[error("Provider error: {0}")]
ProviderError(String),
}
pub trait EnvKey {
fn key() -> &'static str;
}
impl EnvKey for EnvType {
fn key() -> &'static str {
"ENV"
}
}
pub trait AsEnvStr {
fn as_env_str<T: EnvKey>(&self) -> String;
}
impl AsEnvStr for EnvType {
fn as_env_str<T: EnvKey>(&self) -> String {
std::env::var(T::key()).unwrap_or_default()
}
}
pub trait FromKey<V, S> {
fn from_key<K: EnvKey>(value: V) -> S;
}
pub trait AsEnvTypeStr {
fn as_env_type_str(&self) -> Option<String>;
}
impl EnvType {
pub fn from_env() -> Self {
Self::from_env_types::<Self, Self>(Self::default())
}
pub fn from_env_key<K: EnvKey>() -> Self {
Self::from_env_types::<Self, K>(Self::default())
}
pub fn from_env_types<S: AsEnvStr, K: EnvKey>(s: S) -> Self {
Self::from_str(&s.as_env_str::<K>()).unwrap_or_default()
}
pub fn from_env_str<T: AsEnvTypeStr>(t: T) -> Self {
Self::from_str(t.as_env_type_str().unwrap_or_default().as_str()).unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_env_type() {
assert_eq!(EnvType::from_str("Develop").unwrap(), EnvType::Dev);
assert_eq!(EnvType::from_str("develop").unwrap(), EnvType::Dev);
assert_eq!(EnvType::from_str("DEV").unwrap(), EnvType::Dev);
assert_eq!(EnvType::from_str("Dev").unwrap(), EnvType::Dev);
assert_eq!(EnvType::from_str("dev").unwrap(), EnvType::Dev);
assert_eq!(EnvType::from_str("D").unwrap(), EnvType::Dev);
assert_eq!(EnvType::from_str("d").unwrap(), EnvType::Dev);
assert_eq!(EnvType::from_str("TEST").unwrap(), EnvType::Test);
assert_eq!(EnvType::from_str("Test").unwrap(), EnvType::Test);
assert_eq!(EnvType::from_str("test").unwrap(), EnvType::Test);
assert_eq!(EnvType::from_str("T").unwrap(), EnvType::Test);
assert_eq!(EnvType::from_str("t").unwrap(), EnvType::Test);
assert_eq!(EnvType::from_str("Staging").unwrap(), EnvType::Stg);
assert_eq!(EnvType::from_str("staging").unwrap(), EnvType::Stg);
assert_eq!(EnvType::from_str("STG").unwrap(), EnvType::Stg);
assert_eq!(EnvType::from_str("Stg").unwrap(), EnvType::Stg);
assert_eq!(EnvType::from_str("stg").unwrap(), EnvType::Stg);
assert_eq!(EnvType::from_str("S").unwrap(), EnvType::Stg);
assert_eq!(EnvType::from_str("s").unwrap(), EnvType::Stg);
assert_eq!(EnvType::from_str("Production").unwrap(), EnvType::Prod);
assert_eq!(EnvType::from_str("production").unwrap(), EnvType::Prod);
assert_eq!(EnvType::from_str("PROD").unwrap(), EnvType::Prod);
assert_eq!(EnvType::from_str("Prod").unwrap(), EnvType::Prod);
assert_eq!(EnvType::from_str("prod").unwrap(), EnvType::Prod);
assert_eq!(EnvType::from_str("P").unwrap(), EnvType::Prod);
assert_eq!(EnvType::from_str("p").unwrap(), EnvType::Prod);
}
#[test]
fn test_is_debug() {
assert!(EnvType::Dev.is_dev());
assert!(!EnvType::Test.is_dev());
assert!(!EnvType::Stg.is_dev());
assert!(!EnvType::Prod.is_dev());
assert!(EnvType::Test.is_test());
assert!(EnvType::Stg.is_stg());
assert!(EnvType::Prod.is_prod());
}
#[test]
fn test_from_env() {
std::env::set_var("ENV", "d");
assert_eq!(EnvType::from_env(), EnvType::Dev);
struct TestEnv;
impl EnvKey for TestEnv {
fn key() -> &'static str {
"TEST_ENV"
}
}
std::env::set_var("TEST_ENV", "t");
assert_eq!(EnvType::from_env_key::<TestEnv>(), EnvType::Test);
assert_eq!(EnvType::from_env_key::<EnvType>(), EnvType::Dev);
}
#[test]
fn test_from_env_str() {
struct TestEnv(&'static str);
impl AsEnvTypeStr for TestEnv {
fn as_env_type_str(&self) -> Option<String> {
Some(self.0.to_string())
}
}
assert_eq!(EnvType::from_env_str(TestEnv("d")), EnvType::Dev);
assert_eq!(EnvType::from_env_str(TestEnv("t")), EnvType::Test);
assert_eq!(EnvType::from_env_str(TestEnv("s")), EnvType::Stg);
assert_eq!(EnvType::from_env_str(TestEnv("p")), EnvType::Prod);
}
}