tailwind_rs_postcss/advanced_features/
performance_monitor.rs

1//! Performance Monitor
2//!
3//! This module provides comprehensive performance monitoring for
4//! PostCSS operations with metrics collection and analysis.
5
6use super::types::*;
7use std::collections::HashMap;
8use std::time::{Duration, Instant, SystemTime};
9
10/// PostCSS performance monitor
11pub struct PostCSSPerformanceMonitor {
12    metrics: PerformanceMetrics,
13    profiler: PerformanceProfiler,
14    reporter: PerformanceReporter,
15    alerts: Vec<PerformanceAlert>,
16    operation_timers: HashMap<String, Instant>,
17}
18
19impl PostCSSPerformanceMonitor {
20    /// Create new performance monitor
21    pub fn new() -> Self {
22        Self {
23            metrics: PerformanceMetrics {
24                total_time: Duration::from_secs(0),
25                parsing_time: Duration::from_secs(0),
26                transformation_time: Duration::from_secs(0),
27                generation_time: Duration::from_secs(0),
28                memory_usage: 0,
29                cpu_usage: 0.0,
30            },
31            profiler: PerformanceProfiler::new(),
32            reporter: PerformanceReporter::new(),
33            alerts: Vec::new(),
34            operation_timers: HashMap::new(),
35        }
36    }
37
38    /// Start performance monitoring
39    pub fn start_monitoring(&mut self, operation: &str) -> PerformanceTimer {
40        let timer = PerformanceTimer::new(operation.to_string());
41        self.operation_timers
42            .insert(operation.to_string(), Instant::now());
43        timer
44    }
45
46    /// Record performance metrics
47    pub fn record_metrics(&mut self, operation: &str, metrics: OperationMetrics) {
48        // Update overall metrics
49        self.metrics.total_time += metrics.duration;
50        self.metrics.memory_usage = self.metrics.memory_usage.max(metrics.memory_delta as usize);
51        self.metrics.cpu_usage = self.metrics.cpu_usage.max(metrics.cpu_usage);
52
53        // Record operation metrics
54        self.profiler.record_operation(operation, metrics.clone());
55
56        // Check for performance alerts
57        self.check_performance_alerts(operation, &metrics);
58    }
59
60    /// Generate performance report
61    pub fn generate_report(&self) -> PerformanceReport {
62        let operations = self.profiler.get_operation_metrics();
63        let recommendations = self.generate_recommendations(&operations);
64
65        PerformanceReport {
66            total_time: self.metrics.total_time,
67            operations,
68            memory_usage: self.metrics.memory_usage,
69            cpu_usage: self.metrics.cpu_usage,
70            alerts: self.alerts.clone(),
71            recommendations,
72        }
73    }
74
75    /// Monitor processing pipeline
76    pub fn monitor_processing_pipeline(
77        &mut self,
78        css: &str,
79        pipeline: &ProcessingPipeline,
80    ) -> Result<String, AdvancedFeatureError> {
81        let start_time = Instant::now();
82        let start_memory = self.get_memory_usage();
83
84        let mut processed_css = css.to_string();
85
86        for step in &pipeline.steps {
87            let step_timer = self.start_monitoring(&step.name);
88
89            // Execute step
90            processed_css = (step.execute)(&processed_css)?;
91
92            // Record metrics
93            let step_metrics = OperationMetrics {
94                operation: step.name.clone(),
95                duration: step_timer.elapsed(),
96                memory_delta: self.get_memory_usage() as i64 - start_memory as i64,
97                cpu_usage: self.get_cpu_usage(),
98                input_size: step.input_size,
99                output_size: processed_css.len(),
100            };
101
102            self.record_metrics(&step.name, step_metrics);
103        }
104
105        let total_time = start_time.elapsed();
106        let total_memory = self.get_memory_usage();
107
108        // Record overall metrics
109        self.metrics.total_time = total_time;
110        self.metrics.memory_usage = total_memory;
111
112        // Check for performance alerts
113        self.check_performance_alerts(
114            "pipeline",
115            &OperationMetrics {
116                operation: "pipeline".to_string(),
117                duration: total_time,
118                memory_delta: total_memory as i64 - start_memory as i64,
119                cpu_usage: self.get_cpu_usage(),
120                input_size: css.len(),
121                output_size: processed_css.len(),
122            },
123        );
124
125        Ok(processed_css)
126    }
127
128    /// Check for performance alerts
129    fn check_performance_alerts(&mut self, operation: &str, metrics: &OperationMetrics) {
130        // Check for slow operations
131        if metrics.duration > Duration::from_millis(1000) {
132            self.alerts.push(PerformanceAlert {
133                operation: operation.to_string(),
134                issue: "Slow execution".to_string(),
135                severity: AlertSeverity::Medium,
136                timestamp: SystemTime::now(),
137                metrics: metrics.clone(),
138            });
139        }
140
141        // Check for high memory usage
142        if metrics.memory_delta > 100 * 1024 * 1024 {
143            // 100MB
144            self.alerts.push(PerformanceAlert {
145                operation: operation.to_string(),
146                issue: "High memory usage".to_string(),
147                severity: AlertSeverity::High,
148                timestamp: SystemTime::now(),
149                metrics: metrics.clone(),
150            });
151        }
152
153        // Check for high CPU usage
154        if metrics.cpu_usage > 0.8 {
155            self.alerts.push(PerformanceAlert {
156                operation: operation.to_string(),
157                issue: "High CPU usage".to_string(),
158                severity: AlertSeverity::High,
159                timestamp: SystemTime::now(),
160                metrics: metrics.clone(),
161            });
162        }
163    }
164
165    /// Generate performance recommendations
166    fn generate_recommendations(
167        &self,
168        operations: &HashMap<String, OperationMetrics>,
169    ) -> Vec<String> {
170        let mut recommendations = Vec::new();
171
172        // Find slowest operations
173        let mut sorted_operations: Vec<_> = operations.values().collect();
174        sorted_operations.sort_by(|a, b| b.duration.cmp(&a.duration));
175
176        if let Some(slowest) = sorted_operations.first() {
177            if slowest.duration > Duration::from_millis(500) {
178                recommendations.push(format!(
179                    "Consider optimizing '{}' operation (took {:?})",
180                    slowest.operation, slowest.duration
181                ));
182            }
183        }
184
185        // Check memory usage
186        let total_memory: usize = operations
187            .values()
188            .map(|m| m.memory_delta.max(0) as usize)
189            .sum();
190        if total_memory > 50 * 1024 * 1024 {
191            // 50MB
192            recommendations
193                .push("Consider implementing memory optimization strategies".to_string());
194        }
195
196        // Check CPU usage
197        let avg_cpu: f64 =
198            operations.values().map(|m| m.cpu_usage).sum::<f64>() / operations.len() as f64;
199        if avg_cpu > 0.7 {
200            recommendations.push("Consider implementing CPU optimization strategies".to_string());
201        }
202
203        recommendations
204    }
205
206    /// Get current memory usage
207    fn get_memory_usage(&self) -> usize {
208        // Simplified implementation - would use actual memory monitoring
209        0
210    }
211
212    /// Get current CPU usage
213    fn get_cpu_usage(&self) -> f64 {
214        // Simplified implementation - would use actual CPU monitoring
215        0.0
216    }
217}
218
219/// Performance timer
220pub struct PerformanceTimer {
221    operation: String,
222    start_time: Instant,
223}
224
225impl PerformanceTimer {
226    /// Create new performance timer
227    pub fn new(operation: String) -> Self {
228        Self {
229            operation,
230            start_time: Instant::now(),
231        }
232    }
233
234    /// Get elapsed time
235    pub fn elapsed(&self) -> Duration {
236        self.start_time.elapsed()
237    }
238
239    /// Get operation name
240    pub fn operation(&self) -> &str {
241        &self.operation
242    }
243}
244
245/// Performance profiler
246pub struct PerformanceProfiler {
247    operation_metrics: HashMap<String, OperationMetrics>,
248    operation_counts: HashMap<String, usize>,
249}
250
251impl PerformanceProfiler {
252    /// Create new performance profiler
253    pub fn new() -> Self {
254        Self {
255            operation_metrics: HashMap::new(),
256            operation_counts: HashMap::new(),
257        }
258    }
259
260    /// Record operation metrics
261    pub fn record_operation(&mut self, operation: &str, metrics: OperationMetrics) {
262        self.operation_metrics
263            .insert(operation.to_string(), metrics);
264        *self
265            .operation_counts
266            .entry(operation.to_string())
267            .or_insert(0) += 1;
268    }
269
270    /// Get operation metrics
271    pub fn get_operation_metrics(&self) -> HashMap<String, OperationMetrics> {
272        self.operation_metrics.clone()
273    }
274
275    /// Get operation counts
276    pub fn get_operation_counts(&self) -> HashMap<String, usize> {
277        self.operation_counts.clone()
278    }
279}
280
281/// Performance reporter
282pub struct PerformanceReporter;
283
284impl PerformanceReporter {
285    /// Create new performance reporter
286    pub fn new() -> Self {
287        Self
288    }
289}
290
291/// Processing pipeline
292pub struct ProcessingPipeline {
293    pub steps: Vec<ProcessingStep>,
294}
295
296impl ProcessingPipeline {
297    /// Create new processing pipeline
298    pub fn new() -> Self {
299        Self { steps: Vec::new() }
300    }
301
302    /// Add processing step
303    pub fn add_step(&mut self, step: ProcessingStep) {
304        self.steps.push(step);
305    }
306}
307
308/// Processing step
309pub struct ProcessingStep {
310    pub name: String,
311    pub input_size: usize,
312    pub execute: fn(&str) -> Result<String, AdvancedFeatureError>,
313}
314
315impl ProcessingStep {
316    /// Create new processing step
317    pub fn new(
318        name: String,
319        input_size: usize,
320        execute: fn(&str) -> Result<String, AdvancedFeatureError>,
321    ) -> Self {
322        Self {
323            name,
324            input_size,
325            execute,
326        }
327    }
328}
329
330#[cfg(test)]
331mod tests {
332    use super::*;
333
334    #[test]
335    fn test_performance_monitoring() {
336        let mut monitor = PostCSSPerformanceMonitor::new();
337        let timer = monitor.start_monitoring("test_operation");
338
339        // Simulate work
340        std::thread::sleep(Duration::from_millis(100));
341
342        let metrics = OperationMetrics {
343            operation: "test_operation".to_string(),
344            duration: timer.elapsed(),
345            memory_delta: 1024,
346            cpu_usage: 0.5,
347            input_size: 100,
348            output_size: 200,
349        };
350
351        monitor.record_metrics("test_operation", metrics);
352        let report = monitor.generate_report();
353        assert!(report.operations.contains_key("test_operation"));
354    }
355
356    #[test]
357    fn test_performance_timer() {
358        let timer = PerformanceTimer::new("test".to_string());
359        std::thread::sleep(Duration::from_millis(10));
360        assert!(timer.elapsed() >= Duration::from_millis(10));
361        assert_eq!(timer.operation(), "test");
362    }
363
364    #[test]
365    fn test_performance_profiler() {
366        let mut profiler = PerformanceProfiler::new();
367        let metrics = OperationMetrics {
368            operation: "test".to_string(),
369            duration: Duration::from_millis(100),
370            memory_delta: 1024,
371            cpu_usage: 0.5,
372            input_size: 100,
373            output_size: 200,
374        };
375
376        profiler.record_operation("test", metrics);
377        let operation_metrics = profiler.get_operation_metrics();
378        assert!(operation_metrics.contains_key("test"));
379    }
380
381    #[test]
382    fn test_processing_pipeline() {
383        let mut pipeline = ProcessingPipeline::new();
384        let step = ProcessingStep::new("test_step".to_string(), 100, |input| Ok(input.to_string()));
385
386        pipeline.add_step(step);
387        assert_eq!(pipeline.steps.len(), 1);
388    }
389
390    #[test]
391    fn test_performance_alerts() {
392        let mut monitor = PostCSSPerformanceMonitor::new();
393        let slow_metrics = OperationMetrics {
394            operation: "slow_operation".to_string(),
395            duration: Duration::from_millis(2000), // 2 seconds
396            memory_delta: 1024,
397            cpu_usage: 0.5,
398            input_size: 100,
399            output_size: 200,
400        };
401
402        monitor.record_metrics("slow_operation", slow_metrics);
403        let report = monitor.generate_report();
404        assert!(!report.alerts.is_empty());
405    }
406}