#![cfg_attr(coverage_nightly, coverage(off))]
use std::collections::HashSet;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RolloutStrategy {
Disabled,
Allowlist,
Percentage(u32),
FullRollout,
}
pub struct FeatureFlags {
enabled: AtomicBool,
rollout_percentage: AtomicU32,
allowlist: Arc<parking_lot::RwLock<HashSet<String>>>,
max_latency_ms: AtomicU32,
strategy: Arc<parking_lot::RwLock<RolloutStrategy>>,
}
impl Default for FeatureFlags {
fn default() -> Self {
Self::new()
}
}
impl FeatureFlags {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new() -> Self {
Self {
enabled: AtomicBool::new(false), rollout_percentage: AtomicU32::new(0),
allowlist: Arc::new(parking_lot::RwLock::new(HashSet::new())),
max_latency_ms: AtomicU32::new(5000), strategy: Arc::new(parking_lot::RwLock::new(RolloutStrategy::Disabled)),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn should_use_claude(&self, identifier: &str) -> bool {
if !self.enabled.load(Ordering::Relaxed) {
return false;
}
let strategy = *self.strategy.read();
match strategy {
RolloutStrategy::Disabled => false,
RolloutStrategy::Allowlist => self.allowlist.read().contains(identifier),
RolloutStrategy::Percentage(pct) => {
let hash = self.hash_identifier(identifier);
(hash % 100) < pct as u64
}
RolloutStrategy::FullRollout => true,
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn enable(&self) {
self.enabled.store(true, Ordering::Release);
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn disable(&self) {
self.enabled.store(false, Ordering::Release);
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn set_strategy(&self, strategy: RolloutStrategy) {
*self.strategy.write() = strategy;
match strategy {
RolloutStrategy::Disabled => {
self.rollout_percentage.store(0, Ordering::Release);
}
RolloutStrategy::Percentage(pct) => {
self.rollout_percentage.store(pct, Ordering::Release);
}
RolloutStrategy::FullRollout => {
self.rollout_percentage.store(100, Ordering::Release);
}
_ => {}
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn add_to_allowlist(&self, identifier: impl Into<String>) {
self.allowlist.write().insert(identifier.into());
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn remove_from_allowlist(&self, identifier: &str) {
self.allowlist.write().remove(identifier);
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn set_max_latency(&self, latency_ms: u32) {
self.max_latency_ms.store(latency_ms, Ordering::Release);
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn auto_rollback_on_degradation(&self, current_latency_ms: u32) -> bool {
let max = self.max_latency_ms.load(Ordering::Relaxed);
if current_latency_ms > max {
tracing::warn!(
"Performance degradation detected: {}ms > {}ms, disabling Claude integration",
current_latency_ms,
max
);
self.disable();
true
} else {
false
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn get_percentage(&self) -> u32 {
self.rollout_percentage.load(Ordering::Relaxed)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn get_strategy(&self) -> RolloutStrategy {
*self.strategy.read()
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn is_enabled(&self) -> bool {
self.enabled.load(Ordering::Relaxed)
}
fn hash_identifier(&self, identifier: &str) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
identifier.hash(&mut hasher);
hasher.finish()
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn allowlist_size(&self) -> usize {
self.allowlist.read().len()
}
}
pub struct FeatureFlagsBuilder {
enabled: bool,
strategy: RolloutStrategy,
allowlist: HashSet<String>,
max_latency_ms: u32,
}
impl Default for FeatureFlagsBuilder {
fn default() -> Self {
Self {
enabled: false,
strategy: RolloutStrategy::Disabled,
allowlist: HashSet::new(),
max_latency_ms: 5000,
}
}
}
impl FeatureFlagsBuilder {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new() -> Self {
Self::default()
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn strategy(mut self, strategy: RolloutStrategy) -> Self {
self.strategy = strategy;
self
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn add_to_allowlist(mut self, identifier: impl Into<String>) -> Self {
self.allowlist.insert(identifier.into());
self
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn max_latency(mut self, latency_ms: u32) -> Self {
self.max_latency_ms = latency_ms;
self
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn build(self) -> FeatureFlags {
let flags = FeatureFlags::new();
if self.enabled {
flags.enable();
}
flags.set_strategy(self.strategy);
flags.set_max_latency(self.max_latency_ms);
for id in self.allowlist {
flags.add_to_allowlist(id);
}
flags
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_feature_flags_default_disabled() {
let flags = FeatureFlags::new();
assert!(!flags.is_enabled());
assert!(!flags.should_use_claude("test"));
}
#[test]
fn test_enable_disable() {
let flags = FeatureFlags::new();
flags.enable();
assert!(flags.is_enabled());
flags.disable();
assert!(!flags.is_enabled());
}
#[test]
fn test_allowlist_strategy() {
let flags = FeatureFlags::new();
flags.enable();
flags.set_strategy(RolloutStrategy::Allowlist);
assert!(!flags.should_use_claude("user1"));
flags.add_to_allowlist("user1");
assert!(flags.should_use_claude("user1"));
flags.remove_from_allowlist("user1");
assert!(!flags.should_use_claude("user1"));
}
#[test]
fn test_percentage_strategy() {
let flags = FeatureFlags::new();
flags.enable();
flags.set_strategy(RolloutStrategy::Percentage(50));
let id1 = "test_user_1";
let result1 = flags.should_use_claude(id1);
let result2 = flags.should_use_claude(id1);
assert_eq!(result1, result2);
let mut enabled_count = 0;
for i in 0..1000 {
if flags.should_use_claude(&format!("user_{}", i)) {
enabled_count += 1;
}
}
assert!(
enabled_count > 450 && enabled_count < 550,
"Expected ~500, got {}",
enabled_count
);
}
#[test]
fn test_full_rollout_strategy() {
let flags = FeatureFlags::new();
flags.enable();
flags.set_strategy(RolloutStrategy::FullRollout);
assert!(flags.should_use_claude("any_user"));
assert!(flags.should_use_claude("another_user"));
}
#[test]
fn test_auto_rollback() {
let flags = FeatureFlags::new();
flags.enable();
flags.set_max_latency(1000);
assert!(!flags.auto_rollback_on_degradation(500));
assert!(flags.is_enabled());
assert!(flags.auto_rollback_on_degradation(2000));
assert!(!flags.is_enabled());
}
#[test]
fn test_builder() {
let flags = FeatureFlagsBuilder::new()
.enabled(true)
.strategy(RolloutStrategy::Percentage(25))
.add_to_allowlist("special_user")
.max_latency(3000)
.build();
assert!(flags.is_enabled());
assert_eq!(flags.get_percentage(), 25);
assert_eq!(flags.allowlist_size(), 1);
}
#[test]
fn test_kill_switch_overrides_all() {
let flags = FeatureFlags::new();
flags.set_strategy(RolloutStrategy::FullRollout);
flags.add_to_allowlist("user1");
flags.disable();
assert!(!flags.should_use_claude("user1"));
assert!(!flags.should_use_claude("any_user"));
}
}