use crate::enums::AppMessage;
use crate::internal_server_error;
use crate::prelude::AppResult;
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Environment {
#[default]
Local,
Development,
Staging,
Production,
}
impl Environment {
pub fn as_str(&self) -> &'static str {
match self {
Environment::Local => "local",
Environment::Development => "development",
Environment::Staging => "staging",
Environment::Production => "production",
}
}
pub fn as_short_str(&self) -> &'static str {
match self {
Environment::Local => "local",
Environment::Development => "dev",
Environment::Staging => "staging",
Environment::Production => "prod",
}
}
pub fn is_production(&self) -> bool {
matches!(self, Environment::Production)
}
pub fn is_local(&self) -> bool {
matches!(self, Environment::Local)
}
pub fn is_dev_like(&self) -> bool {
matches!(self, Environment::Local | Environment::Development)
}
pub fn allows_debug(&self) -> bool {
!self.is_production()
}
pub fn from_env(var_name: &str) -> AppResult<Environment> {
std::env::var(var_name)
.map_err(|e| {
AppMessage::MissingEnvironmentVariable(var_name.to_string(), e).into_anyhow()
})
.and_then(|val: String| val.parse())
}
pub fn from_env_or_default(var_name: &str, default: Environment) -> Environment {
std::env::var(var_name)
.ok()
.and_then(|val| val.parse().ok())
.unwrap_or(default)
}
pub fn all() -> &'static [Environment] {
&[
Environment::Local,
Environment::Development,
Environment::Staging,
Environment::Production,
]
}
}
impl fmt::Display for Environment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl FromStr for Environment {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"local" => Ok(Environment::Local),
"development" | "dev" => Ok(Environment::Development),
"staging" | "stage" => Ok(Environment::Staging),
"production" | "prod" => Ok(Environment::Production),
_ => Err(internal_server_error!(
"Invalid environment value: '{s}'. Valid values are: local, development (dev), staging (stage), production (prod)"
)),
}
}
}
mod serde_impl {
use super::Environment;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
impl Serialize for Environment {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for Environment {
fn deserialize<D>(deserializer: D) -> Result<Environment, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Environment::from_str(&s).map_err(serde::de::Error::custom)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_as_str() {
assert_eq!(Environment::Local.as_str(), "local");
assert_eq!(Environment::Development.as_str(), "development");
assert_eq!(Environment::Staging.as_str(), "staging");
assert_eq!(Environment::Production.as_str(), "production");
}
#[test]
fn test_as_short_str() {
assert_eq!(Environment::Local.as_short_str(), "local");
assert_eq!(Environment::Development.as_short_str(), "dev");
assert_eq!(Environment::Staging.as_short_str(), "staging");
assert_eq!(Environment::Production.as_short_str(), "prod");
}
#[test]
fn test_is_production() {
assert!(!Environment::Local.is_production());
assert!(!Environment::Development.is_production());
assert!(!Environment::Staging.is_production());
assert!(Environment::Production.is_production());
}
#[test]
fn test_is_dev_like() {
assert!(Environment::Local.is_dev_like());
assert!(Environment::Development.is_dev_like());
assert!(!Environment::Staging.is_dev_like());
assert!(!Environment::Production.is_dev_like());
}
#[test]
fn test_from_str() {
assert_eq!("local".parse::<Environment>().unwrap(), Environment::Local);
assert_eq!(
"development".parse::<Environment>().unwrap(),
Environment::Development
);
assert_eq!(
"dev".parse::<Environment>().unwrap(),
Environment::Development
);
assert_eq!(
"staging".parse::<Environment>().unwrap(),
Environment::Staging
);
assert_eq!(
"stage".parse::<Environment>().unwrap(),
Environment::Staging
);
assert_eq!(
"production".parse::<Environment>().unwrap(),
Environment::Production
);
assert_eq!(
"prod".parse::<Environment>().unwrap(),
Environment::Production
);
assert_eq!(
"PRODUCTION".parse::<Environment>().unwrap(),
Environment::Production
);
assert_eq!(
"Dev".parse::<Environment>().unwrap(),
Environment::Development
);
assert!("invalid".parse::<Environment>().is_err());
}
#[test]
fn test_display() {
assert_eq!(format!("{}", Environment::Local), "local");
assert_eq!(format!("{}", Environment::Production), "production");
}
#[test]
fn test_default() {
assert_eq!(Environment::default(), Environment::Local);
}
#[test]
fn test_all() {
let all = Environment::all();
assert_eq!(all.len(), 4);
assert!(all.contains(&Environment::Local));
assert!(all.contains(&Environment::Development));
assert!(all.contains(&Environment::Staging));
assert!(all.contains(&Environment::Production));
}
}