use std::time::{Duration, Instant};
use tracing::{info, warn};
#[derive(Debug, Clone)]
pub struct SafetyConfig {
pub min_available_mb: f64,
pub min_interval: Duration,
pub max_processes_per_run: usize,
pub dry_run: bool,
pub protected_processes: Vec<String>,
}
impl Default for SafetyConfig {
fn default() -> Self {
Self {
min_available_mb: 1024.0, min_interval: Duration::from_secs(30),
max_processes_per_run: 50,
dry_run: false,
protected_processes: vec![
"system".into(),
"csrss.exe".into(),
"smss.exe".into(),
"lsass.exe".into(),
"services.exe".into(),
"wininit.exe".into(),
"winlogon.exe".into(),
"dwm.exe".into(),
"explorer.exe".into(),
"msmpeng.exe".into(), "securityhealthservice.exe".into(),
"avgnt.exe".into(),
"avp.exe".into(),
],
}
}
}
pub struct SafetyGuard {
config: SafetyConfig,
last_optimization: Option<Instant>,
consecutive_failures: usize,
total_optimizations: usize,
}
impl SafetyGuard {
pub fn new(config: SafetyConfig) -> Self {
Self {
config,
last_optimization: None,
consecutive_failures: 0,
total_optimizations: 0,
}
}
pub fn check_safe(&self, current_available_mb: f64) -> Result<(), String> {
if current_available_mb < self.config.min_available_mb {
return Err(format!(
"Available memory ({:.0}MB) below safety floor ({:.0}MB)",
current_available_mb, self.config.min_available_mb
));
}
if let Some(last) = self.last_optimization {
let elapsed = last.elapsed();
if elapsed < self.config.min_interval {
return Err(format!(
"Rate limited: {:?} remaining",
self.config.min_interval - elapsed
));
}
}
if self.consecutive_failures >= 3 {
return Err(format!(
"Too many consecutive failures ({}). Manual intervention needed.",
self.consecutive_failures
));
}
Ok(())
}
pub fn is_protected(&self, process_name: &str) -> bool {
let name_lower = process_name.to_lowercase();
self.config.protected_processes.iter()
.any(|p| name_lower.contains(&p.to_lowercase()))
}
pub fn record_attempt(&mut self, success: bool) {
self.last_optimization = Some(Instant::now());
self.total_optimizations += 1;
if success {
self.consecutive_failures = 0;
} else {
self.consecutive_failures += 1;
warn!("Optimization failed. Consecutive failures: {}", self.consecutive_failures);
}
}
pub fn is_dry_run(&self) -> bool {
self.config.dry_run
}
pub fn max_processes(&self) -> usize {
self.config.max_processes_per_run
}
pub fn emergency_stop(&mut self) {
self.consecutive_failures = 100; warn!("Emergency stop activated - optimizations disabled");
}
pub fn reset(&mut self) {
self.consecutive_failures = 0;
self.last_optimization = None;
info!("Safety counters reset");
}
pub fn stats(&self) -> SafetyStats {
SafetyStats {
total_optimizations: self.total_optimizations,
consecutive_failures: self.consecutive_failures,
is_healthy: self.consecutive_failures < 3,
}
}
}
#[derive(Debug, Clone)]
pub struct SafetyStats {
pub total_optimizations: usize,
pub consecutive_failures: usize,
pub is_healthy: bool,
}