Skip to main content

memory_monitor/
monitor.rs

1// File: src\monitor.rs
2// Author: Hadi Cahyadi <cumulus13@gmail.com>
3// Date: 2026-05-06
4// Description: 
5// License: MIT
6
7use chrono::Local;
8use crossterm::{
9    cursor,
10    terminal::{self, Clear, ClearType},
11    ExecutableCommand,
12};
13use sysinfo::{System, SystemExt};
14use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
15use std::thread;
16use std::time::Duration;
17
18use crate::config::MonitorConfig;
19use crate::notification::GntpNotifier;
20use crate::cleaner::MemoryCleaner;
21
22pub struct MemoryMonitor {
23    pub config: MonitorConfig,
24    pub notifier: Option<GntpNotifier>,
25    cleaner: MemoryCleaner,
26    system: System,
27}
28
29impl MemoryMonitor {
30    pub fn new() -> Self {
31        let config = MonitorConfig::load("config.ini");
32        let notifier = GntpNotifier::new(
33            &config.growl_host,
34            config.growl_port,
35            &config.growl_password,
36            &config.growl_app_name,
37            &config.icon_path,
38        );
39        let cleaner = MemoryCleaner::new(&config.cleanmem_path);
40        
41        MemoryMonitor {
42            config,
43            notifier,
44            cleaner,
45            system: System::new_all(),
46        }
47    }
48    
49    #[allow(dead_code)]
50    pub fn with_config(config: MonitorConfig) -> Self {
51        let notifier = GntpNotifier::new(
52            &config.growl_host,
53            config.growl_port,
54            &config.growl_password,
55            &config.growl_app_name,
56            &config.icon_path,
57        );
58        let cleaner = MemoryCleaner::new(&config.cleanmem_path);
59        
60        MemoryMonitor {
61            config,
62            notifier,
63            cleaner,
64            system: System::new_all(),
65        }
66    }
67    
68    pub fn get_memory_usage(&mut self) -> f64 {
69        self.system.refresh_memory();
70        let total = self.system.total_memory() as f64;
71        let used = self.system.used_memory() as f64;
72        (used / total) * 100.0
73    }
74
75    pub fn threshold(&self) -> f64 {
76        self.config.threshold
77    }
78
79    pub fn check_interval(&self) -> u64 {
80        self.config.check_interval
81    }
82
83    pub fn cleanmem_path(&self) -> &str {
84        &self.config.cleanmem_path
85    }
86
87    pub fn growl_host(&self) -> &str {
88        &self.config.growl_host
89    }
90
91    pub fn growl_port(&self) -> u16 {
92        self.config.growl_port
93    }
94
95    pub fn growl_app_name(&self) -> &str {
96        &self.config.growl_app_name
97    }
98
99    pub fn has_notifier(&self) -> bool {
100        self.notifier.is_some()
101    }
102    
103    fn display_status(&self, memory_percent: f64) {
104        let mut stdout = StandardStream::stdout(ColorChoice::Always);
105        let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S");
106        
107        let (color, emoji, status) = if memory_percent >= self.config.threshold {
108            (Color::Red, "๐Ÿ”ด", "CRITICAL")
109        } else if memory_percent >= self.config.threshold - 5.0 {
110            (Color::Yellow, "๐ŸŸก", "WARNING")
111        } else {
112            (Color::Green, "๐ŸŸข", "OK")
113        };
114        
115        stdout.execute(Clear(ClearType::All)).ok();
116        stdout.execute(cursor::MoveTo(0, 0)).ok();
117        
118        let width = terminal::size().map(|(w, _)| w as usize).unwrap_or(80);
119        
120        stdout.set_color(ColorSpec::new().set_fg(Some(Color::Magenta)).set_bold(true)).ok();
121        println!("{}", "=".repeat(width));
122        println!("        ๐Ÿš€ Memory Monitor & Auto-Cleaner");
123        println!("{}", "=".repeat(width));
124        stdout.reset().ok();
125        
126        stdout.set_color(ColorSpec::new().set_fg(Some(color)).set_bold(true)).ok();
127        println!("\n{} Memory Status: {}", emoji, status);
128        stdout.reset().ok();
129        
130        println!("โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”");
131        
132        stdout.set_color(ColorSpec::new().set_fg(Some(color))).ok();
133        println!("๐Ÿ“Š Usage: {:.2}%", memory_percent);
134        stdout.reset().ok();
135        
136        println!("๐ŸŽฏ Threshold: {}%", self.config.threshold);
137        println!("โฐ Interval : {} {}", self.config.check_interval, 
138            if self.config.check_interval < 10 { "second" } else { "seconds" });
139        println!("โฐ Time: {}", timestamp);
140    }
141    
142    pub fn monitor(&mut self) {
143        let mut stdout = StandardStream::stdout(ColorChoice::Always);
144        let width = terminal::size().map(|(w, _)| w as usize).unwrap_or(80);
145        
146        stdout.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)).set_bold(true)).ok();
147        println!("{}", "โ”€".repeat(width));
148        println!("๐Ÿ’“ Memory Monitor Started");
149        println!("Threshold: {}% | Interval: {}s", self.config.threshold, self.config.check_interval);
150        println!("{}", "โ”€".repeat(width));
151        stdout.reset().ok();
152        
153        loop {
154            let memory_percent = self.get_memory_usage();
155            self.display_status(memory_percent);
156            
157            if memory_percent >= self.config.threshold {
158                stdout.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true)).ok();
159                println!("\nโš ๏ธ  ALERT: Memory usage at {:.2}%!\n", memory_percent);
160                stdout.reset().ok();
161                
162                let success = self.cleaner.clean();
163                
164                if let Some(ref notifier) = self.notifier {
165                    if success {
166                        notifier.notify(
167                            "๐Ÿงน Memory Cleaned",
168                            &format!("Memory was at {:.2}%. CleanMem executed successfully.", memory_percent),
169                            0,
170                        );
171                    } else {
172                        notifier.notify(
173                            "๐Ÿ”ด Memory Critical",
174                            &format!("Memory at {:.2}%. CleanMem execution failed!", memory_percent),
175                            2,
176                        );
177                    }
178                }
179                
180                thread::sleep(Duration::from_secs(30));
181                
182                let new_memory = self.get_memory_usage();
183                stdout.set_color(ColorSpec::new().set_fg(Some(Color::Cyan))).ok();
184                println!("๐Ÿ“‰ Memory after cleanup: {:.2}%\n", new_memory);
185                stdout.reset().ok();
186            }
187            
188            thread::sleep(Duration::from_secs(self.config.check_interval));
189        }
190    }
191}