leptos-helios 0.8.1

High-performance Rust visualization library with Canvas2D, WebGPU, and WebAssembly support
Documentation
//! Performance Monitoring and Optimization
//!
//! This module provides performance monitoring and optimization for accessibility.

use super::AccessibilityError;
use std::collections::HashMap;
use std::time::{Duration, Instant};

/// Performance configuration
#[derive(Debug, Clone)]
pub struct PerformanceConfig {
    pub max_execution_time: Duration,
    pub memory_limit: usize,
    pub enable_monitoring: bool,
}

/// Performance monitor for accessibility features
pub struct PerformanceMonitor {
    config: PerformanceConfig,
    metrics: HashMap<String, PerformanceMetric>,
    active_operations: HashMap<String, Instant>,
}

/// Performance metric
#[derive(Debug, Clone)]
pub struct PerformanceMetric {
    pub operation_name: String,
    pub duration_ms: u64,
    pub memory_usage_mb: f64,
    pub timestamp: Instant,
    pub success: bool,
}

/// Caching configuration for performance
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CachingConfig {
    pub enabled: bool,
    pub max_cache_size_mb: u64,
    pub ttl_seconds: u64,
    pub cache_strategy: CacheStrategy,
    pub preload_common_queries: bool,
}

impl Default for CachingConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            max_cache_size_mb: 100,
            ttl_seconds: 300, // 5 minutes
            cache_strategy: CacheStrategy::LRU,
            preload_common_queries: true,
        }
    }
}

/// Cache strategy
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum CacheStrategy {
    LRU,
    FIFO,
    TTL,
}

/// Monitoring configuration
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct MonitoringConfig {
    pub enabled: bool,
    pub sample_rate: f64, // 0.0 to 1.0
    pub alert_threshold_ms: u64,
    pub memory_alert_threshold_mb: u64,
    pub log_performance: bool,
}

impl Default for MonitoringConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            sample_rate: 1.0,
            alert_threshold_ms: 100,
            memory_alert_threshold_mb: 50,
            log_performance: true,
        }
    }
}

impl PerformanceMonitor {
    /// Create a new performance monitor
    pub fn new(config: PerformanceConfig) -> Self {
        Self {
            config,
            metrics: HashMap::new(),
            active_operations: HashMap::new(),
        }
    }

    /// Start monitoring an operation
    pub fn start_monitoring(&mut self, operation_name: &str) -> PerformanceMonitor {
        if !self.config.enable_monitoring {
            return PerformanceMonitor {
                config: self.config.clone(),
                metrics: HashMap::new(),
                active_operations: HashMap::new(),
            };
        }

        self.active_operations
            .insert(operation_name.to_string(), Instant::now());

        PerformanceMonitor {
            config: self.config.clone(),
            metrics: HashMap::new(),
            active_operations: HashMap::new(),
        }
    }

    /// End monitoring an operation
    pub fn end_monitoring(
        &mut self,
        operation_name: &str,
        success: bool,
    ) -> Result<PerformanceMetric, AccessibilityError> {
        if !self.config.enable_monitoring {
            return Err(AccessibilityError::PerformanceBudgetExceeded(
                "Performance monitoring is disabled".to_string(),
            ));
        }

        let start_time = self
            .active_operations
            .remove(operation_name)
            .ok_or_else(|| {
                AccessibilityError::PerformanceBudgetExceeded(format!(
                    "No active operation found for: {}",
                    operation_name
                ))
            })?;

        let duration = start_time.elapsed();
        let duration_ms = duration.as_millis() as u64;

        // Check if operation exceeded budget
        if duration_ms > self.config.max_execution_time.as_millis() as u64 {
            return Err(AccessibilityError::PerformanceBudgetExceeded(format!(
                "Operation '{}' exceeded budget: {}ms > {}ms",
                operation_name,
                duration_ms,
                self.config.max_execution_time.as_millis()
            )));
        }

        let metric = PerformanceMetric {
            operation_name: operation_name.to_string(),
            duration_ms,
            memory_usage_mb: self.get_memory_usage(),
            timestamp: Instant::now(),
            success,
        };

        self.metrics
            .insert(operation_name.to_string(), metric.clone());

        // Log performance if enabled
        if self.config.enable_monitoring {
            self.log_performance(&metric);
        }

        Ok(metric)
    }

    /// Get performance metrics
    pub fn get_metrics(&self) -> &HashMap<String, PerformanceMetric> {
        &self.metrics
    }

    /// Get average performance for an operation
    pub fn get_average_performance(&self, operation_name: &str) -> Option<f64> {
        let metrics: Vec<&PerformanceMetric> = self
            .metrics
            .values()
            .filter(|m| m.operation_name == operation_name)
            .collect();

        if metrics.is_empty() {
            return None;
        }

        let total_duration: u64 = metrics.iter().map(|m| m.duration_ms).sum();
        Some(total_duration as f64 / metrics.len() as f64)
    }

    /// Check if performance budget is being met
    pub fn check_budget_compliance(&self) -> BudgetComplianceReport {
        let mut report = BudgetComplianceReport {
            overall_compliant: true,
            violations: Vec::new(),
            warnings: Vec::new(),
            recommendations: Vec::new(),
        };

        for metric in self.metrics.values() {
            if metric.duration_ms > self.config.max_execution_time.as_millis() as u64 {
                report.overall_compliant = false;
                report.violations.push(BudgetViolation {
                    operation: metric.operation_name.clone(),
                    actual_ms: metric.duration_ms,
                    budget_ms: self.config.max_execution_time.as_millis() as u64,
                    severity: if metric.duration_ms
                        > self.config.max_execution_time.as_millis() as u64 * 2
                    {
                        PerformanceViolationSeverity::Critical
                    } else {
                        PerformanceViolationSeverity::High
                    },
                });
            } else if metric.duration_ms
                > (self.config.max_execution_time.as_millis() as u64 * 8) / 10
            {
                report.warnings.push(format!(
                    "Operation '{}' is approaching budget limit: {}ms / {}ms",
                    metric.operation_name,
                    metric.duration_ms,
                    self.config.max_execution_time.as_millis()
                ));
            }
        }

        // Generate recommendations
        if !report.overall_compliant {
            report
                .recommendations
                .push("Consider optimizing slow operations".to_string());
            report
                .recommendations
                .push("Enable caching for frequently accessed data".to_string());
            report
                .recommendations
                .push("Implement lazy loading for large datasets".to_string());
        }

        report
    }

    /// Get memory usage (simplified implementation)
    fn get_memory_usage(&self) -> f64 {
        // In a real implementation, this would use system APIs to get actual memory usage
        // For now, return a placeholder value
        0.0
    }

    /// Log performance metric
    fn log_performance(&self, metric: &PerformanceMetric) {
        println!(
            "Performance: {} took {}ms (success: {})",
            metric.operation_name, metric.duration_ms, metric.success
        );
    }
}

/// Budget compliance report
#[derive(Debug, Clone)]
pub struct BudgetComplianceReport {
    pub overall_compliant: bool,
    pub violations: Vec<BudgetViolation>,
    pub warnings: Vec<String>,
    pub recommendations: Vec<String>,
}

/// Budget violation
#[derive(Debug, Clone)]
pub struct BudgetViolation {
    pub operation: String,
    pub actual_ms: u64,
    pub budget_ms: u64,
    pub severity: PerformanceViolationSeverity,
}

/// Performance violation severity
#[derive(Debug, Clone)]
pub enum PerformanceViolationSeverity {
    Low,
    Medium,
    High,
    Critical,
}

/// Performance optimization manager
pub struct PerformanceOptimizer {
    cache: HashMap<String, CachedResult>,
    config: CachingConfig,
}

/// Cached result
#[derive(Debug, Clone)]
pub struct CachedResult {
    pub data: String,
    pub timestamp: Instant,
    pub ttl: Duration,
}

impl PerformanceOptimizer {
    /// Create a new performance optimizer
    pub fn new(config: CachingConfig) -> Self {
        Self {
            cache: HashMap::new(),
            config,
        }
    }

    /// Get cached result
    pub fn get_cached(&self, key: &str) -> Option<&String> {
        if !self.config.enabled {
            return None;
        }

        if let Some(cached) = self.cache.get(key) {
            if cached.timestamp.elapsed() < cached.ttl {
                return Some(&cached.data);
            }
        }

        None
    }

    /// Cache a result
    pub fn cache_result(&mut self, key: String, data: String) {
        if !self.config.enabled {
            return;
        }

        let ttl = Duration::from_secs(self.config.ttl_seconds);
        let cached = CachedResult {
            data,
            timestamp: Instant::now(),
            ttl,
        };

        self.cache.insert(key, cached);
        self.cleanup_expired();
    }

    /// Clean up expired cache entries
    fn cleanup_expired(&mut self) {
        let now = Instant::now();
        self.cache
            .retain(|_, cached| now.duration_since(cached.timestamp) < cached.ttl);
    }

    /// Clear cache
    pub fn clear_cache(&mut self) {
        self.cache.clear();
    }

    /// Get cache statistics
    pub fn get_cache_stats(&self) -> CacheStats {
        let total_entries = self.cache.len();
        let total_size_mb = self
            .cache
            .values()
            .map(|c| c.data.len() as f64 / 1024.0 / 1024.0)
            .sum();

        CacheStats {
            total_entries,
            total_size_mb,
            hit_rate: 0.0, // Would need to track hits/misses
        }
    }
}

/// Cache statistics
#[derive(Debug, Clone)]
pub struct CacheStats {
    pub total_entries: usize,
    pub total_size_mb: f64,
    pub hit_rate: f64,
}