videocall_types/
feature_flags.rs1use std::sync::atomic::{AtomicU8, Ordering};
20use std::sync::OnceLock;
21
22const ENV_PREFIX: &str = "FEATURE_";
24
25const OVERRIDE_NONE: u8 = 0;
27const OVERRIDE_TRUE: u8 = 1;
28const OVERRIDE_FALSE: u8 = 2;
29
30static MEETING_MANAGEMENT_OVERRIDE: AtomicU8 = AtomicU8::new(OVERRIDE_NONE);
32
33#[derive(Debug, Clone)]
35pub struct FeatureFlags {
36 pub meeting_management: bool,
39
40 pub database: bool,
43}
44
45impl FeatureFlags {
46 fn from_env() -> Self {
48 Self {
49 meeting_management: read_bool_env("MEETING_MANAGEMENT"),
50 database: read_bool_no_prefix("DATABASE_ENABLED"),
51 }
52 }
53
54 pub fn global() -> &'static Self {
57 static FLAGS: OnceLock<FeatureFlags> = OnceLock::new();
58 FLAGS.get_or_init(FeatureFlags::from_env)
59 }
60
61 #[inline]
64 pub fn meeting_management_enabled() -> bool {
65 match MEETING_MANAGEMENT_OVERRIDE.load(Ordering::SeqCst) {
66 OVERRIDE_TRUE => true,
67 OVERRIDE_FALSE => false,
68 _ => Self::global().meeting_management,
69 }
70 }
71
72 #[inline]
74 pub fn database_enabled() -> bool {
75 Self::global().database
76 }
77
78 #[cfg(any(test, feature = "testing"))]
83 pub fn set_meeting_management_override(enabled: bool) {
84 let value = if enabled {
85 OVERRIDE_TRUE
86 } else {
87 OVERRIDE_FALSE
88 };
89 MEETING_MANAGEMENT_OVERRIDE.store(value, Ordering::SeqCst);
90 }
91
92 #[cfg(any(test, feature = "testing"))]
96 pub fn clear_meeting_management_override() {
97 MEETING_MANAGEMENT_OVERRIDE.store(OVERRIDE_NONE, Ordering::SeqCst);
98 }
99}
100
101fn read_bool_env(name: &str) -> bool {
104 let full_name = format!("{ENV_PREFIX}{name}");
105 std::env::var(&full_name)
106 .map(|v| matches!(v.to_lowercase().as_str(), "true" | "1" | "yes"))
107 .unwrap_or(false)
108}
109
110fn read_bool_no_prefix(name: &str) -> bool {
113 std::env::var(name)
114 .map(|v| matches!(v.to_lowercase().as_str(), "true" | "1" | "yes"))
115 .unwrap_or(false)
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_read_bool_env_not_set() {
124 std::env::remove_var("FEATURE_TEST_FLAG");
126 assert!(!read_bool_env("TEST_FLAG"));
127 }
128
129 #[test]
130 fn test_read_bool_env_truthy_values() {
131 std::env::set_var("FEATURE_TEST_TRUE", "true");
132 assert!(read_bool_env("TEST_TRUE"));
133
134 std::env::set_var("FEATURE_TEST_ONE", "1");
135 assert!(read_bool_env("TEST_ONE"));
136
137 std::env::set_var("FEATURE_TEST_YES", "yes");
138 assert!(read_bool_env("TEST_YES"));
139
140 std::env::set_var("FEATURE_TEST_TRUE_UPPER", "TRUE");
141 assert!(read_bool_env("TEST_TRUE_UPPER"));
142
143 std::env::remove_var("FEATURE_TEST_TRUE");
145 std::env::remove_var("FEATURE_TEST_ONE");
146 std::env::remove_var("FEATURE_TEST_YES");
147 std::env::remove_var("FEATURE_TEST_TRUE_UPPER");
148 }
149
150 #[test]
151 fn test_read_bool_env_falsy_values() {
152 std::env::set_var("FEATURE_TEST_FALSE", "false");
153 assert!(!read_bool_env("TEST_FALSE"));
154
155 std::env::set_var("FEATURE_TEST_ZERO", "0");
156 assert!(!read_bool_env("TEST_ZERO"));
157
158 std::env::set_var("FEATURE_TEST_RANDOM", "random");
159 assert!(!read_bool_env("TEST_RANDOM"));
160
161 std::env::remove_var("FEATURE_TEST_FALSE");
163 std::env::remove_var("FEATURE_TEST_ZERO");
164 std::env::remove_var("FEATURE_TEST_RANDOM");
165 }
166}