use cm_dashboard_shared::{CacheConfig, Metric}; use std::collections::HashMap; use std::fs; use std::path::Path; use std::sync::Arc; use tokio::sync::RwLock; use tracing::{info, warn, error}; /// Simple persistent cache for metrics pub struct SimpleCache { metrics: RwLock>, persist_path: String, } impl SimpleCache { pub fn new(config: CacheConfig) -> Self { let cache = Self { metrics: RwLock::new(HashMap::new()), persist_path: config.persist_path, }; // Clear cache file on startup to ensure fresh data cache.clear_cache_file(); cache } /// Store metric in cache pub async fn store_metric(&self, metric: Metric) { let mut metrics = self.metrics.write().await; metrics.insert(metric.name.clone(), metric); } /// Get all cached metrics pub async fn get_all_cached_metrics(&self) -> Vec { let metrics = self.metrics.read().await; metrics.values().cloned().collect() } /// Save cache to disk pub async fn save_to_disk(&self) { let metrics = self.metrics.read().await; // Create directory if needed if let Some(parent) = Path::new(&self.persist_path).parent() { if let Err(e) = fs::create_dir_all(parent) { warn!("Failed to create cache directory {}: {}", parent.display(), e); return; } } // Serialize and save match serde_json::to_string_pretty(&*metrics) { Ok(json) => { if let Err(e) = fs::write(&self.persist_path, json) { error!("Failed to save cache to {}: {}", self.persist_path, e); } } Err(e) => { error!("Failed to serialize cache: {}", e); } } } /// Load cache from disk fn load_from_disk(&self) { match fs::read_to_string(&self.persist_path) { Ok(content) => { match serde_json::from_str::>(&content) { Ok(loaded_metrics) => { if let Ok(mut metrics) = self.metrics.try_write() { *metrics = loaded_metrics; info!("Loaded {} metrics from cache", metrics.len()); } } Err(e) => { warn!("Failed to parse cache file {}: {}", self.persist_path, e); } } } Err(_) => { info!("No cache file found at {}, starting fresh", self.persist_path); } } } /// Clear cache file on startup to ensure fresh data fn clear_cache_file(&self) { if Path::new(&self.persist_path).exists() { match fs::remove_file(&self.persist_path) { Ok(_) => info!("Cleared cache file {} on startup", self.persist_path), Err(e) => warn!("Failed to clear cache file {}: {}", self.persist_path, e), } } } } #[derive(Clone)] pub struct MetricCacheManager { cache: Arc, } impl MetricCacheManager { pub fn new(config: CacheConfig) -> Self { Self { cache: Arc::new(SimpleCache::new(config)), } } pub async fn store_metric(&self, metric: Metric) { self.cache.store_metric(metric).await; } pub async fn cache_metric(&self, metric: Metric) { self.store_metric(metric).await; } pub async fn start_background_tasks(&self) { // No background tasks needed for simple cache } pub async fn get_all_cached_metrics(&self) -> Result, anyhow::Error> { Ok(self.cache.get_all_cached_metrics().await) } pub async fn save_to_disk(&self) { self.cache.save_to_disk().await; } }