1pub mod logger;
4
5use crate::options::{DeploymentMode, RustAuthOptions};
6
7pub fn env_var(suffix: &str) -> Option<String> {
11 let key = format!("RUSTAUTH_{suffix}");
12 std::env::var(&key).ok().filter(|value| !value.is_empty())
13}
14
15pub fn is_production() -> bool {
17 std::env::var("RUST_ENV").is_ok_and(|value| value == "production")
18}
19
20fn is_development_env() -> bool {
22 match std::env::var("RUST_ENV") {
23 Ok(value) => value == "development" || value == "test",
24 Err(_) => is_test_runtime(),
25 }
26}
27
28fn is_test_runtime() -> bool {
29 std::env::var("RUST_TEST_THREADS").is_ok()
30 || std::env::var("NEXTEST").is_ok_and(|value| value == "1")
31 || std::env::var("TEST")
32 .is_ok_and(|value| !value.is_empty() && value != "0" && value.to_lowercase() != "false")
33}
34
35pub fn is_production_posture(options: &RustAuthOptions) -> bool {
40 !allows_development_defaults(options)
41}
42
43pub fn allows_development_defaults(options: &RustAuthOptions) -> bool {
45 match options.mode {
46 DeploymentMode::Production => false,
47 DeploymentMode::Development => !is_production(),
48 DeploymentMode::Auto => !is_production() && is_development_env(),
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use std::sync::{Mutex, MutexGuard, OnceLock};
55
56 use super::*;
57
58 struct EnvRestore(Vec<(&'static str, Option<String>)>);
59
60 impl EnvRestore {
61 fn unset(keys: &[&'static str]) -> Self {
62 let saved = keys
63 .iter()
64 .map(|key| (*key, std::env::var(key).ok()))
65 .collect::<Vec<_>>();
66 for key in keys {
67 std::env::remove_var(key);
68 }
69 Self(saved)
70 }
71 }
72
73 impl Drop for EnvRestore {
74 fn drop(&mut self) {
75 for (key, value) in &self.0 {
76 match value {
77 Some(value) => std::env::set_var(key, value),
78 None => std::env::remove_var(key),
79 }
80 }
81 }
82 }
83
84 fn env_lock() -> &'static Mutex<()> {
85 static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
86 LOCK.get_or_init(|| Mutex::new(()))
87 }
88
89 fn lock_env() -> MutexGuard<'static, ()> {
90 env_lock()
91 .lock()
92 .unwrap_or_else(|poisoned| poisoned.into_inner())
93 }
94
95 #[test]
96 fn env_var_reads_rustauth_prefix() {
97 let _guard = lock_env();
98 let _restore = EnvRestore::unset(&["RUSTAUTH_SECRET"]);
99 std::env::set_var("RUSTAUTH_SECRET", "rustauth-secret");
100
101 assert_eq!(env_var("SECRET").as_deref(), Some("rustauth-secret"));
102 }
103
104 #[test]
105 fn env_var_ignores_empty_values() {
106 let _guard = lock_env();
107 let _restore = EnvRestore::unset(&["RUSTAUTH_SECRET"]);
108 std::env::set_var("RUSTAUTH_SECRET", "");
109
110 assert_eq!(env_var("SECRET"), None);
111 }
112
113 #[test]
114 fn env_var_returns_none_when_unset() {
115 let _guard = lock_env();
116 let _restore = EnvRestore::unset(&["RUSTAUTH_SECRET"]);
117
118 assert_eq!(env_var("SECRET"), None);
119 }
120}