use crate::error::{Error, Result};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::convert::TryFrom;
use std::fmt;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Environment {
Prod,
Stage,
Test,
Dev,
Local,
}
impl<'de> Deserialize<'de> for Environment {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct EnvironmentVisitor;
impl de::Visitor<'_> for EnvironmentVisitor {
type Value = Environment;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("any valid environment")
}
fn visit_str<E>(self, value: &str) -> std::result::Result<Environment, E>
where
E: de::Error,
{
TryFrom::try_from(value).map_err(de::Error::custom)
}
}
deserializer.deserialize_string(EnvironmentVisitor)
}
}
impl Serialize for Environment {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl fmt::Display for Environment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let env = match *self {
Environment::Prod => "prod",
Environment::Stage => "stage",
Environment::Test => "test",
Environment::Dev => "dev",
Environment::Local => "local",
};
write!(f, "{env}")
}
}
impl TryFrom<&str> for Environment {
type Error = Error;
fn try_from(env: &str) -> Result<Self> {
match env {
"prod" => Ok(Environment::Prod),
"stage" => Ok(Environment::Stage),
"test" => Ok(Environment::Test),
"dev" => Ok(Environment::Dev),
"local" => Ok(Environment::Local),
_ => Err(Error::invalid_runtime_environment(env)),
}
}
}
impl TryFrom<String> for Environment {
type Error = Error;
fn try_from(env: String) -> Result<Self> {
match &env[..] {
"prod" => Ok(Environment::Prod),
"stage" => Ok(Environment::Stage),
"test" => Ok(Environment::Test),
"dev" => Ok(Environment::Dev),
"local" => Ok(Environment::Local),
_ => Err(Error::invalid_runtime_environment(&env)),
}
}
}
#[cfg(test)]
mod test {
use super::Environment;
use std::convert::TryFrom;
#[test]
fn display() {
assert_eq!(Environment::Prod.to_string(), "prod");
assert_eq!(Environment::Stage.to_string(), "stage");
assert_eq!(Environment::Test.to_string(), "test");
assert_eq!(Environment::Dev.to_string(), "dev");
assert_eq!(Environment::Local.to_string(), "local");
}
#[test]
fn convert() {
match Environment::try_from("prod") {
Ok(re) => assert_eq!(re, Environment::Prod),
Err(_) => assert!(false, "Invalid 'prod' Runtime Environment"),
}
match Environment::try_from("stage") {
Ok(re) => assert_eq!(re, Environment::Stage),
Err(_) => assert!(false, "Invalid 'stage' Runtime Environment"),
}
match Environment::try_from("test") {
Ok(re) => assert_eq!(re, Environment::Test),
Err(_) => assert!(false, "Invalid 'test' Runtime Environment"),
}
match Environment::try_from("dev") {
Ok(re) => assert_eq!(re, Environment::Dev),
Err(_) => assert!(false, "Invalid 'dev' Runtime Environment"),
}
match Environment::try_from("blah") {
Ok(_) => assert!(false, "'blah' is not a good runtime environment!"),
Err(_) => assert!(true, "'blah' failed to convert properly"),
}
}
}