#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Mode {
Dev,
Prod,
#[default]
Auto,
}
impl Mode {
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
Mode::Dev => "dev",
Mode::Prod => "prod",
Mode::Auto => "auto",
}
}
#[must_use]
pub fn resolve(self) -> Self {
match self {
Mode::Auto => detect_from_env(),
other => other,
}
}
}
#[must_use]
pub fn current() -> Mode {
detect_from_env()
}
fn detect_from_env() -> Mode {
if let Some(m) = read("FSYS_MODE") {
return m;
}
if let Some(m) = read("RUST_ENV") {
return m;
}
Mode::Prod
}
fn read(var: &str) -> Option<Mode> {
let raw = std::env::var(var).ok()?;
parse(&raw)
}
fn parse(raw: &str) -> Option<Mode> {
let normalised = raw.trim().to_ascii_lowercase();
match normalised.as_str() {
"dev" | "development" | "debug" => Some(Mode::Dev),
"prod" | "production" | "release" => Some(Mode::Prod),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_is_auto() {
assert_eq!(Mode::default(), Mode::Auto);
}
#[test]
fn test_as_str_round_trips() {
assert_eq!(Mode::Dev.as_str(), "dev");
assert_eq!(Mode::Prod.as_str(), "prod");
assert_eq!(Mode::Auto.as_str(), "auto");
}
#[test]
fn test_resolve_concrete_returns_self() {
assert_eq!(Mode::Dev.resolve(), Mode::Dev);
assert_eq!(Mode::Prod.resolve(), Mode::Prod);
}
#[test]
fn test_resolve_auto_returns_concrete() {
let m = Mode::Auto.resolve();
assert!(matches!(m, Mode::Dev | Mode::Prod));
}
#[test]
fn test_parse_recognises_dev_synonyms() {
assert_eq!(parse("dev"), Some(Mode::Dev));
assert_eq!(parse("DEV"), Some(Mode::Dev));
assert_eq!(parse("development"), Some(Mode::Dev));
assert_eq!(parse("debug"), Some(Mode::Dev));
assert_eq!(parse(" dev "), Some(Mode::Dev));
}
#[test]
fn test_parse_recognises_prod_synonyms() {
assert_eq!(parse("prod"), Some(Mode::Prod));
assert_eq!(parse("PROD"), Some(Mode::Prod));
assert_eq!(parse("production"), Some(Mode::Prod));
assert_eq!(parse("release"), Some(Mode::Prod));
}
#[test]
fn test_parse_rejects_unknown() {
assert!(parse("staging").is_none());
assert!(parse("").is_none());
assert!(parse("garbage").is_none());
}
}