1use console::style;
5use log::{info, warn};
6use std::path::Path;
7use std::time::Duration;
8
9use crate::config::OrbitonConfig;
10use crate::dev_server::DevServer;
11use crate::hmr::HmrContext;
12
13pub struct MaintenanceManager {
15 hmr_context: HmrContext,
16 config: OrbitonConfig,
17}
18
19impl MaintenanceManager {
20 pub fn new(project_dir: &Path) -> anyhow::Result<Self> {
22 let config = OrbitonConfig::load_from_project(project_dir)?;
23 let hmr_context = HmrContext::new(project_dir.to_path_buf());
24
25 Ok(Self {
26 hmr_context,
27 config,
28 })
29 }
30
31 pub fn cleanup_stale_updates(&self, max_age: Duration) {
33 info!("Cleaning up stale HMR updates older than {max_age:?}");
34
35 let stale_modules = self.hmr_context.get_stale_updates(max_age);
36 if !stale_modules.is_empty() {
37 println!(
38 "{} Removing {} stale modules: {}",
39 style("Cleanup:").bold().yellow(),
40 stale_modules.len(),
41 stale_modules.join(", ")
42 );
43
44 self.hmr_context.clear_stale_updates(max_age);
45 } else {
46 println!("{} No stale modules found", style("Info:").bold().blue());
47 }
48 }
49
50 pub fn clear_all_updates(&self) {
52 info!("Clearing all pending HMR updates");
53 self.hmr_context.clear();
54 println!(
55 "{} All HMR updates cleared",
56 style("Cleanup:").bold().green()
57 );
58 }
59 #[allow(dead_code)] pub fn get_update_info(&self) -> Option<Duration> {
62 let oldest_age = self.hmr_context.get_oldest_update_age();
63 if let Some(age) = oldest_age {
64 println!(
65 "{} Oldest pending update is {:?} old",
66 style("Info:").bold().blue(),
67 age
68 );
69 } else {
70 println!("{} No pending updates", style("Info:").bold().blue());
71 }
72 oldest_age
73 }
74 #[allow(dead_code)] pub fn apply_config_overrides(&mut self, overrides: OrbitonConfig) {
77 info!("Applying configuration overrides");
78 self.config.merge_with(&overrides);
79
80 println!(
81 "{} Configuration updated with overrides",
82 style("Config:").bold().green()
83 );
84 }
85 #[allow(dead_code)] pub fn create_simple_dev_server(
88 &self,
89 port: u16,
90 project_dir: &Path,
91 ) -> anyhow::Result<DevServer> {
92 info!("Creating simple development server on port {port}");
93 DevServer::new(port, project_dir)
94 }
95 #[allow(dead_code)] pub fn perform_automated_maintenance(&self) -> anyhow::Result<()> {
98 info!("Performing automated maintenance");
99
100 let cleanup_threshold = Duration::from_millis(self.config.hmr.debounce_ms * 300); self.cleanup_stale_updates(cleanup_threshold);
105
106 self.get_update_info();
108
109 let pending_count = self.hmr_context.get_pending_updates().len();
111 if pending_count > 10 {
112 warn!(
113 "High number of pending updates ({pending_count}), consider restarting the dev server"
114 );
115
116 println!(
117 "{} {} pending updates - consider restarting for optimal performance",
118 style("Warning:").bold().yellow(),
119 pending_count
120 );
121 }
122
123 Ok(())
124 }
125
126 pub fn show_status(&self) {
128 info!("Displaying maintenance status");
129
130 println!("{}", style("=== Maintenance Status ===").bold().cyan());
131
132 let pending_updates = self.hmr_context.get_pending_updates();
134 println!(
135 "{} {} pending HMR updates",
136 style("HMR:").bold().blue(),
137 pending_updates.len()
138 );
139
140 if !pending_updates.is_empty() {
141 println!(" Modules: {}", pending_updates.join(", "));
142
143 if let Some(oldest_age) = self.hmr_context.get_oldest_update_age() {
144 println!(" Oldest update: {oldest_age:?} ago");
145 }
146 }
147
148 println!(
150 "{} Port: {}, HMR: {}",
151 style("Config:").bold().blue(),
152 self.config.dev_server.port,
153 if self.config.hmr.enabled {
154 "enabled"
155 } else {
156 "disabled"
157 }
158 );
159 println!(
160 " Debounce: {}ms, Source dir: {}",
161 self.config.hmr.debounce_ms, self.config.project.src_dir
162 );
163
164 let stale_threshold = Duration::from_secs(300); let stale_updates = self.hmr_context.get_stale_updates(stale_threshold);
167 if !stale_updates.is_empty() {
168 println!(
169 "{} {} stale updates (older than 5 minutes)",
170 style("Warning:").bold().yellow(),
171 stale_updates.len()
172 );
173 }
174
175 println!("{}", style("==========================").bold().cyan());
176 }
177 #[allow(dead_code)] pub fn config(&self) -> &OrbitonConfig {
180 &self.config
181 }
182
183 #[allow(dead_code)] pub fn hmr_context(&self) -> &HmrContext {
186 &self.hmr_context
187 }
188}
189
190#[allow(dead_code)] pub fn perform_project_maintenance(project_dir: &Path) -> anyhow::Result<()> {
193 let manager = MaintenanceManager::new(project_dir)?;
194 manager.perform_automated_maintenance()
195}
196
197#[allow(dead_code)] pub fn demo_config_merging(project_dir: &Path) -> anyhow::Result<()> {
200 let mut manager = MaintenanceManager::new(project_dir)?;
201
202 let mut override_config = OrbitonConfig::default();
204 override_config.dev_server.port = 9000;
205 override_config.hmr.enabled = false;
206 override_config.hmr.debounce_ms = 1000;
207
208 println!(
209 "{} Original port: {}",
210 style("Before:").bold().blue(),
211 manager.config().dev_server.port
212 );
213
214 manager.apply_config_overrides(override_config);
215
216 println!(
217 "{} New port: {}",
218 style("After:").bold().green(),
219 manager.config().dev_server.port
220 );
221
222 Ok(())
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 use std::fs;
229 use tempfile::tempdir;
230
231 #[test]
232 fn test_maintenance_manager_creation() {
233 let temp_dir = tempdir().unwrap();
234 let result = MaintenanceManager::new(temp_dir.path());
235 assert!(result.is_ok());
236 }
237
238 #[test]
239 fn test_hmr_cleanup() {
240 let temp_dir = tempdir().unwrap();
241 let project_dir = temp_dir.path();
242
243 let src_dir = project_dir.join("src");
245 fs::create_dir_all(&src_dir).unwrap();
246 let test_file = src_dir.join("test.rs");
247 fs::write(&test_file, "// test").unwrap();
248
249 let manager = MaintenanceManager::new(project_dir).unwrap();
250
251 manager.hmr_context().record_file_change(&test_file);
253
254 manager.cleanup_stale_updates(Duration::from_secs(1));
256 manager.clear_all_updates();
257 manager.get_update_info();
258 }
259
260 #[test]
261 fn test_config_merging() {
262 let temp_dir = tempdir().unwrap();
263 let project_dir = temp_dir.path();
264
265 let mut manager = MaintenanceManager::new(project_dir).unwrap();
266 let original_port = manager.config().dev_server.port;
267
268 let mut override_config = OrbitonConfig::default();
269 override_config.dev_server.port = 9999;
270
271 manager.apply_config_overrides(override_config);
272
273 assert_ne!(manager.config().dev_server.port, original_port);
274 assert_eq!(manager.config().dev_server.port, 9999);
275 }
276
277 #[test]
278 fn test_simple_dev_server_creation() {
279 let temp_dir = tempdir().unwrap();
280 let project_dir = temp_dir.path();
281
282 let manager = MaintenanceManager::new(project_dir).unwrap();
283 let result = manager.create_simple_dev_server(0, project_dir); assert!(result.is_ok());
285 }
286}