flash_watcher/
stats.rs

1use std::time::{Duration, Instant};
2
3use chrono::Local;
4use colored::Colorize;
5use sysinfo::{Pid, System};
6
7/// Stats collector for Flash performance metrics
8pub struct StatsCollector {
9    pub start_time: Instant,
10    pub file_changes: usize,
11    pub watcher_calls: usize,
12    pub last_memory_usage: u64,
13    pub last_cpu_usage: f32,
14    system: System,
15}
16
17impl Default for StatsCollector {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl StatsCollector {
24    pub fn new() -> Self {
25        Self {
26            start_time: Instant::now(),
27            file_changes: 0,
28            watcher_calls: 0,
29            last_memory_usage: 0,
30            last_cpu_usage: 0.0,
31            system: System::new_all(),
32        }
33    }
34
35    pub fn record_file_change(&mut self) {
36        self.file_changes += 1;
37    }
38
39    pub fn record_watcher_call(&mut self) {
40        self.watcher_calls += 1;
41    }
42
43    pub fn update_resource_usage(&mut self) {
44        self.system.refresh_all();
45
46        let pid = std::process::id();
47        if let Some(process) = self.system.process(Pid::from_u32(pid)) {
48            self.last_memory_usage = process.memory() / 1024; // KB
49            self.last_cpu_usage = process.cpu_usage();
50        }
51    }
52
53    pub fn display_stats(&self) {
54        let elapsed = self.start_time.elapsed();
55        let timestamp = Local::now().format("%H:%M:%S").to_string();
56
57        println!("{}", "── Flash Performance Stats ──".bright_green());
58        println!("{} {}", "Time:".bright_blue(), timestamp);
59        println!("{} {}", "Uptime:".bright_blue(), format_duration(elapsed));
60        println!("{} {}", "File changes:".bright_blue(), self.file_changes);
61        println!("{} {}", "Watcher calls:".bright_blue(), self.watcher_calls);
62        println!(
63            "{} {} KB",
64            "Memory usage:".bright_blue(),
65            self.last_memory_usage
66        );
67        println!("{} {:.1}%", "CPU usage:".bright_blue(), self.last_cpu_usage);
68        println!("{}", "────────────────────────────".bright_green());
69    }
70}
71
72pub fn format_duration(duration: Duration) -> String {
73    let seconds = duration.as_secs();
74    if seconds < 60 {
75        format!("{}s", seconds)
76    } else if seconds < 3600 {
77        format!("{}m {}s", seconds / 60, seconds % 60)
78    } else {
79        format!(
80            "{}h {}m {}s",
81            seconds / 3600,
82            (seconds % 3600) / 60,
83            seconds % 60
84        )
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use std::time::Duration;
92
93    #[test]
94    fn test_stats_collector_new() {
95        let stats = StatsCollector::new();
96        assert_eq!(stats.file_changes, 0);
97        assert_eq!(stats.watcher_calls, 0);
98        assert_eq!(stats.last_memory_usage, 0);
99        assert_eq!(stats.last_cpu_usage, 0.0);
100    }
101
102    #[test]
103    fn test_record_file_change() {
104        let mut stats = StatsCollector::new();
105        assert_eq!(stats.file_changes, 0);
106
107        stats.record_file_change();
108        assert_eq!(stats.file_changes, 1);
109
110        stats.record_file_change();
111        assert_eq!(stats.file_changes, 2);
112    }
113
114    #[test]
115    fn test_record_watcher_call() {
116        let mut stats = StatsCollector::new();
117        assert_eq!(stats.watcher_calls, 0);
118
119        stats.record_watcher_call();
120        assert_eq!(stats.watcher_calls, 1);
121
122        stats.record_watcher_call();
123        assert_eq!(stats.watcher_calls, 2);
124    }
125
126    #[test]
127    fn test_format_duration_seconds() {
128        assert_eq!(format_duration(Duration::from_secs(0)), "0s");
129        assert_eq!(format_duration(Duration::from_secs(30)), "30s");
130        assert_eq!(format_duration(Duration::from_secs(59)), "59s");
131    }
132
133    #[test]
134    fn test_format_duration_minutes() {
135        assert_eq!(format_duration(Duration::from_secs(60)), "1m 0s");
136        assert_eq!(format_duration(Duration::from_secs(90)), "1m 30s");
137        assert_eq!(format_duration(Duration::from_secs(3599)), "59m 59s");
138    }
139
140    #[test]
141    fn test_format_duration_hours() {
142        assert_eq!(format_duration(Duration::from_secs(3600)), "1h 0m 0s");
143        assert_eq!(format_duration(Duration::from_secs(3661)), "1h 1m 1s");
144        assert_eq!(format_duration(Duration::from_secs(7323)), "2h 2m 3s");
145    }
146
147    #[test]
148    fn test_update_resource_usage() {
149        let mut stats = StatsCollector::new();
150        // This test just ensures the method doesn't panic
151        // Actual values depend on system state
152        stats.update_resource_usage();
153        // Memory usage should be updated (non-zero for a running process)
154        // Note: This might be 0 in some test environments, so we just check it doesn't panic
155    }
156}