oxihuman_core/
feature_flag.rs1#![allow(dead_code)]
4
5use std::collections::HashMap;
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum FlagState {
12 Enabled,
13 Disabled,
14 Experimental,
15}
16
17#[derive(Debug, Clone)]
19pub struct FeatureFlagEntry {
20 pub name: String,
21 pub state: FlagState,
22 pub description: String,
23}
24
25#[derive(Debug, Default)]
27pub struct FeatureFlagRegistry {
28 flags: HashMap<String, FeatureFlagEntry>,
29}
30
31impl FeatureFlagRegistry {
32 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn register(&mut self, name: &str, enabled: bool, description: &str) {
37 let state = if enabled {
38 FlagState::Enabled
39 } else {
40 FlagState::Disabled
41 };
42 self.flags.insert(
43 name.to_string(),
44 FeatureFlagEntry {
45 name: name.to_string(),
46 state,
47 description: description.to_string(),
48 },
49 );
50 }
51
52 pub fn toggle(&mut self, name: &str) -> bool {
53 if let Some(entry) = self.flags.get_mut(name) {
54 entry.state = match entry.state {
55 FlagState::Enabled => FlagState::Disabled,
56 FlagState::Disabled | FlagState::Experimental => FlagState::Enabled,
57 };
58 true
59 } else {
60 false
61 }
62 }
63
64 pub fn is_enabled(&self, name: &str) -> bool {
65 self.flags
66 .get(name)
67 .map(|e| e.state == FlagState::Enabled)
68 .unwrap_or(false)
69 }
70
71 pub fn flag_count(&self) -> usize {
72 self.flags.len()
73 }
74
75 pub fn all_names(&self) -> Vec<String> {
76 let mut names: Vec<String> = self.flags.keys().cloned().collect();
77 names.sort();
78 names
79 }
80}
81
82pub fn new_flag_registry() -> FeatureFlagRegistry {
83 FeatureFlagRegistry::new()
84}
85
86pub fn register_feature(registry: &mut FeatureFlagRegistry, name: &str, enabled: bool, desc: &str) {
87 registry.register(name, enabled, desc);
88}
89
90pub fn toggle_feature(registry: &mut FeatureFlagRegistry, name: &str) -> bool {
91 registry.toggle(name)
92}
93
94pub fn is_feature_enabled(registry: &FeatureFlagRegistry, name: &str) -> bool {
95 registry.is_enabled(name)
96}
97
98pub fn feature_flag_count(registry: &FeatureFlagRegistry) -> usize {
99 registry.flag_count()
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn test_register_and_check() {
108 let mut reg = new_flag_registry();
109 register_feature(&mut reg, "dark_mode", true, "Dark theme");
110 assert!(is_feature_enabled(®, "dark_mode"));
111 }
112
113 #[test]
114 fn test_disabled_flag() {
115 let mut reg = new_flag_registry();
116 register_feature(&mut reg, "beta", false, "Beta feature");
117 assert!(!is_feature_enabled(®, "beta"));
118 }
119
120 #[test]
121 fn test_toggle_on_to_off() {
122 let mut reg = new_flag_registry();
123 register_feature(&mut reg, "foo", true, "Foo");
124 toggle_feature(&mut reg, "foo");
125 assert!(!is_feature_enabled(®, "foo"));
126 }
127
128 #[test]
129 fn test_toggle_off_to_on() {
130 let mut reg = new_flag_registry();
131 register_feature(&mut reg, "bar", false, "Bar");
132 toggle_feature(&mut reg, "bar");
133 assert!(is_feature_enabled(®, "bar"));
134 }
135
136 #[test]
137 fn test_toggle_unknown_returns_false() {
138 let mut reg = new_flag_registry();
139 assert!(!toggle_feature(&mut reg, "nonexistent"));
140 }
141
142 #[test]
143 fn test_flag_count() {
144 let mut reg = new_flag_registry();
145 register_feature(&mut reg, "a", true, "A");
146 register_feature(&mut reg, "b", false, "B");
147 assert_eq!(feature_flag_count(®), 2);
148 }
149
150 #[test]
151 fn test_all_names_sorted() {
152 let mut reg = new_flag_registry();
153 register_feature(&mut reg, "zeta", true, "Z");
154 register_feature(&mut reg, "alpha", true, "A");
155 let names = reg.all_names();
156 assert_eq!(names[0], "alpha");
157 }
158
159 #[test]
160 fn test_unknown_flag_disabled() {
161 let reg = new_flag_registry();
162 assert!(!is_feature_enabled(®, "missing"));
163 }
164
165 #[test]
166 fn test_experimental_state() {
167 let mut reg = new_flag_registry();
169 reg.flags.insert(
170 "exp".to_string(),
171 FeatureFlagEntry {
172 name: "exp".to_string(),
173 state: FlagState::Experimental,
174 description: "Experimental".to_string(),
175 },
176 );
177 toggle_feature(&mut reg, "exp");
178 assert!(is_feature_enabled(®, "exp"));
179 }
180
181 #[test]
182 fn test_overwrite_flag() {
183 let mut reg = new_flag_registry();
185 register_feature(&mut reg, "x", false, "off");
186 register_feature(&mut reg, "x", true, "on");
187 assert!(is_feature_enabled(®, "x"));
188 }
189}