#![allow(dead_code)]
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub enum FlagState {
Enabled,
Disabled,
Experimental,
}
#[derive(Debug, Clone)]
pub struct FeatureFlagEntry {
pub name: String,
pub state: FlagState,
pub description: String,
}
#[derive(Debug, Default)]
pub struct FeatureFlagRegistry {
flags: HashMap<String, FeatureFlagEntry>,
}
impl FeatureFlagRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, name: &str, enabled: bool, description: &str) {
let state = if enabled {
FlagState::Enabled
} else {
FlagState::Disabled
};
self.flags.insert(
name.to_string(),
FeatureFlagEntry {
name: name.to_string(),
state,
description: description.to_string(),
},
);
}
pub fn toggle(&mut self, name: &str) -> bool {
if let Some(entry) = self.flags.get_mut(name) {
entry.state = match entry.state {
FlagState::Enabled => FlagState::Disabled,
FlagState::Disabled | FlagState::Experimental => FlagState::Enabled,
};
true
} else {
false
}
}
pub fn is_enabled(&self, name: &str) -> bool {
self.flags
.get(name)
.map(|e| e.state == FlagState::Enabled)
.unwrap_or(false)
}
pub fn flag_count(&self) -> usize {
self.flags.len()
}
pub fn all_names(&self) -> Vec<String> {
let mut names: Vec<String> = self.flags.keys().cloned().collect();
names.sort();
names
}
}
pub fn new_flag_registry() -> FeatureFlagRegistry {
FeatureFlagRegistry::new()
}
pub fn register_feature(registry: &mut FeatureFlagRegistry, name: &str, enabled: bool, desc: &str) {
registry.register(name, enabled, desc);
}
pub fn toggle_feature(registry: &mut FeatureFlagRegistry, name: &str) -> bool {
registry.toggle(name)
}
pub fn is_feature_enabled(registry: &FeatureFlagRegistry, name: &str) -> bool {
registry.is_enabled(name)
}
pub fn feature_flag_count(registry: &FeatureFlagRegistry) -> usize {
registry.flag_count()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_register_and_check() {
let mut reg = new_flag_registry();
register_feature(&mut reg, "dark_mode", true, "Dark theme");
assert!(is_feature_enabled(®, "dark_mode"));
}
#[test]
fn test_disabled_flag() {
let mut reg = new_flag_registry();
register_feature(&mut reg, "beta", false, "Beta feature");
assert!(!is_feature_enabled(®, "beta"));
}
#[test]
fn test_toggle_on_to_off() {
let mut reg = new_flag_registry();
register_feature(&mut reg, "foo", true, "Foo");
toggle_feature(&mut reg, "foo");
assert!(!is_feature_enabled(®, "foo"));
}
#[test]
fn test_toggle_off_to_on() {
let mut reg = new_flag_registry();
register_feature(&mut reg, "bar", false, "Bar");
toggle_feature(&mut reg, "bar");
assert!(is_feature_enabled(®, "bar"));
}
#[test]
fn test_toggle_unknown_returns_false() {
let mut reg = new_flag_registry();
assert!(!toggle_feature(&mut reg, "nonexistent"));
}
#[test]
fn test_flag_count() {
let mut reg = new_flag_registry();
register_feature(&mut reg, "a", true, "A");
register_feature(&mut reg, "b", false, "B");
assert_eq!(feature_flag_count(®), 2);
}
#[test]
fn test_all_names_sorted() {
let mut reg = new_flag_registry();
register_feature(&mut reg, "zeta", true, "Z");
register_feature(&mut reg, "alpha", true, "A");
let names = reg.all_names();
assert_eq!(names[0], "alpha");
}
#[test]
fn test_unknown_flag_disabled() {
let reg = new_flag_registry();
assert!(!is_feature_enabled(®, "missing"));
}
#[test]
fn test_experimental_state() {
let mut reg = new_flag_registry();
reg.flags.insert(
"exp".to_string(),
FeatureFlagEntry {
name: "exp".to_string(),
state: FlagState::Experimental,
description: "Experimental".to_string(),
},
);
toggle_feature(&mut reg, "exp");
assert!(is_feature_enabled(®, "exp"));
}
#[test]
fn test_overwrite_flag() {
let mut reg = new_flag_registry();
register_feature(&mut reg, "x", false, "off");
register_feature(&mut reg, "x", true, "on");
assert!(is_feature_enabled(®, "x"));
}
}