use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::OnceLock;
const ENV_PREFIX: &str = "FEATURE_";
const OVERRIDE_NONE: u8 = 0;
const OVERRIDE_TRUE: u8 = 1;
const OVERRIDE_FALSE: u8 = 2;
static MEETING_MANAGEMENT_OVERRIDE: AtomicU8 = AtomicU8::new(OVERRIDE_NONE);
#[derive(Debug, Clone)]
pub struct FeatureFlags {
pub meeting_management: bool,
pub database: bool,
}
impl FeatureFlags {
fn from_env() -> Self {
Self {
meeting_management: read_bool_env("MEETING_MANAGEMENT"),
database: read_bool_no_prefix("DATABASE_ENABLED"),
}
}
pub fn global() -> &'static Self {
static FLAGS: OnceLock<FeatureFlags> = OnceLock::new();
FLAGS.get_or_init(FeatureFlags::from_env)
}
#[inline]
pub fn meeting_management_enabled() -> bool {
match MEETING_MANAGEMENT_OVERRIDE.load(Ordering::SeqCst) {
OVERRIDE_TRUE => true,
OVERRIDE_FALSE => false,
_ => Self::global().meeting_management,
}
}
#[inline]
pub fn database_enabled() -> bool {
Self::global().database
}
#[cfg(any(test, feature = "testing"))]
pub fn set_meeting_management_override(enabled: bool) {
let value = if enabled {
OVERRIDE_TRUE
} else {
OVERRIDE_FALSE
};
MEETING_MANAGEMENT_OVERRIDE.store(value, Ordering::SeqCst);
}
#[cfg(any(test, feature = "testing"))]
pub fn clear_meeting_management_override() {
MEETING_MANAGEMENT_OVERRIDE.store(OVERRIDE_NONE, Ordering::SeqCst);
}
}
fn read_bool_env(name: &str) -> bool {
let full_name = format!("{ENV_PREFIX}{name}");
std::env::var(&full_name)
.map(|v| matches!(v.to_lowercase().as_str(), "true" | "1" | "yes"))
.unwrap_or(false)
}
fn read_bool_no_prefix(name: &str) -> bool {
std::env::var(name)
.map(|v| matches!(v.to_lowercase().as_str(), "true" | "1" | "yes"))
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_read_bool_env_not_set() {
std::env::remove_var("FEATURE_TEST_FLAG");
assert!(!read_bool_env("TEST_FLAG"));
}
#[test]
fn test_read_bool_env_truthy_values() {
std::env::set_var("FEATURE_TEST_TRUE", "true");
assert!(read_bool_env("TEST_TRUE"));
std::env::set_var("FEATURE_TEST_ONE", "1");
assert!(read_bool_env("TEST_ONE"));
std::env::set_var("FEATURE_TEST_YES", "yes");
assert!(read_bool_env("TEST_YES"));
std::env::set_var("FEATURE_TEST_TRUE_UPPER", "TRUE");
assert!(read_bool_env("TEST_TRUE_UPPER"));
std::env::remove_var("FEATURE_TEST_TRUE");
std::env::remove_var("FEATURE_TEST_ONE");
std::env::remove_var("FEATURE_TEST_YES");
std::env::remove_var("FEATURE_TEST_TRUE_UPPER");
}
#[test]
fn test_read_bool_env_falsy_values() {
std::env::set_var("FEATURE_TEST_FALSE", "false");
assert!(!read_bool_env("TEST_FALSE"));
std::env::set_var("FEATURE_TEST_ZERO", "0");
assert!(!read_bool_env("TEST_ZERO"));
std::env::set_var("FEATURE_TEST_RANDOM", "random");
assert!(!read_bool_env("TEST_RANDOM"));
std::env::remove_var("FEATURE_TEST_FALSE");
std::env::remove_var("FEATURE_TEST_ZERO");
std::env::remove_var("FEATURE_TEST_RANDOM");
}
}