use crate::envx::EnvironmentError;
use serde::{Deserialize, Serialize};
use std::env;
const ENV_VAR: &str = "ENVIRONMENT";
const LOCAL: &str = "local";
const TEST: &str = "test";
const STAGING: &str = "staging";
const PRODUCTION: &str = "production";
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum Environment {
Local,
Test,
Staging,
Production,
}
impl Environment {
pub fn detect() -> Result<Environment, EnvironmentError> {
match env::var(ENV_VAR).ok() {
Some(env) => Environment::try_from(env),
None => Err(EnvironmentError::EnvNotFound(ENV_VAR.to_string())),
}
}
pub fn is_local(&self) -> bool {
matches!(self, Environment::Local)
}
pub fn is_test(&self) -> bool {
matches!(self, Environment::Test)
}
pub fn is_staging(&self) -> bool {
matches!(self, Environment::Staging)
}
pub fn is_production(&self) -> bool {
matches!(self, Environment::Production)
}
pub fn is_deployed(&self) -> bool {
matches!(self, Environment::Staging | Environment::Production)
}
pub fn get_name(&self) -> String {
format!("{self:?}").to_lowercase()
}
}
impl<'a> TryFrom<&'a str> for Environment {
type Error = EnvironmentError;
fn try_from(env: &'a str) -> Result<Self, Self::Error> {
from_string(env)
}
}
impl TryFrom<String> for Environment {
type Error = EnvironmentError;
fn try_from(env: String) -> Result<Self, Self::Error> {
from_string(env)
}
}
fn from_string(env: impl AsRef<str>) -> Result<Environment, EnvironmentError> {
match env.as_ref().to_lowercase().as_str() {
LOCAL => Ok(Environment::Local),
TEST => Ok(Environment::Test),
STAGING => Ok(Environment::Staging),
PRODUCTION => Ok(Environment::Production),
_ => Err(EnvironmentError::UnknownEnvironment(
env.as_ref().to_string(),
)),
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn from_string() {
assert!(matches!(
Environment::try_from("local"),
Ok(Environment::Local)
));
assert!(matches!(
Environment::try_from("test"),
Ok(Environment::Test)
));
assert!(matches!(
Environment::try_from("staging"),
Ok(Environment::Staging)
));
assert!(matches!(
Environment::try_from("production"),
Ok(Environment::Production)
));
let unknown = String::from("unknown");
assert!(
matches!(Environment::try_from(unknown.clone()), Err(EnvironmentError::UnknownEnvironment(value)) if value == unknown)
);
}
#[test]
fn get_true_for_specific_environments() {
let test_data = [
(Environment::Local, true, false, false, false, false),
(Environment::Test, false, true, false, false, false),
(Environment::Staging, false, false, true, false, true),
(Environment::Production, false, false, false, true, true),
];
for (env, local, test, staging, production, deployed) in test_data.iter() {
assert_eq!(env.is_local(), *local);
assert_eq!(env.is_test(), *test);
assert_eq!(env.is_staging(), *staging);
assert_eq!(env.is_production(), *production);
assert_eq!(env.is_deployed(), *deployed);
}
}
#[test]
fn detect_environment_from_env_var() {
let test_data = [
(LOCAL, Environment::Local),
(TEST, Environment::Test),
(STAGING, Environment::Staging),
(PRODUCTION, Environment::Production),
];
for (env, expected) in test_data.iter() {
env::remove_var(ENV_VAR);
env::set_var(ENV_VAR, env);
let current_env = Environment::detect().expect("failed to detect environment");
assert_eq!(current_env, *expected);
}
env::remove_var(ENV_VAR);
}
}