use std::sync::atomic::{AtomicBool, Ordering};
pub const DEFAULT_ENV_ID: &str = "local";
pub const LEGACY_ENV_ID: &str = "dev";
pub const DISABLE_ALIAS_ENV_VAR: &str = "GREENTIC_DISABLE_DEV_ALIAS";
static WARNED: AtomicBool = AtomicBool::new(false);
#[must_use]
pub fn apply_dev_alias(env: &str) -> String {
if env != LEGACY_ENV_ID {
return env.to_string();
}
assert!(
!alias_disabled(),
"environment `{LEGACY_ENV_ID}` is no longer accepted (set via {DISABLE_ALIAS_ENV_VAR}=1). \
Migrate to `{DEFAULT_ENV_ID}`, or pass `--env {DEFAULT_ENV_ID}` / unset $GREENTIC_ENV."
);
if !WARNED.swap(true, Ordering::SeqCst) {
eprintln!(
"[greentic] env `{LEGACY_ENV_ID}` is deprecated; resolving as `{DEFAULT_ENV_ID}` for this \
process. Set {DISABLE_ALIAS_ENV_VAR}=1 to hard-fail on `{LEGACY_ENV_ID}`."
);
}
DEFAULT_ENV_ID.to_string()
}
#[must_use]
pub fn resolve_env(override_env: Option<&str>) -> String {
let raw = override_env
.map(ToString::to_string)
.or_else(|| std::env::var("GREENTIC_ENV").ok())
.unwrap_or_else(|| DEFAULT_ENV_ID.to_string());
apply_dev_alias(&raw)
}
fn alias_disabled() -> bool {
std::env::var(DISABLE_ALIAS_ENV_VAR)
.ok()
.map(|v| {
matches!(
v.trim().to_ascii_lowercase().as_str(),
"1" | "true" | "yes" | "on"
)
})
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn non_legacy_passthrough() {
assert_eq!(apply_dev_alias("local"), "local");
assert_eq!(apply_dev_alias("prod"), "prod");
}
#[test]
fn legacy_dev_remaps_to_local() {
assert_eq!(apply_dev_alias(LEGACY_ENV_ID), DEFAULT_ENV_ID);
}
#[test]
fn resolve_prefers_override_then_default() {
assert_eq!(resolve_env(Some("staging")), "staging");
assert_eq!(resolve_env(Some(LEGACY_ENV_ID)), DEFAULT_ENV_ID);
}
}