1use colored::Colorize;
17use std::process::Command;
18
19pub const RESTART_DELAY_MS: u64 = 500;
21
22#[derive(Debug, PartialEq, Clone)]
24pub struct ProcessResult {
25 pub success: bool,
26 pub message: String,
27}
28
29impl ProcessResult {
30 pub fn success(message: impl Into<String>) -> Self {
31 Self {
32 success: true,
33 message: message.into(),
34 }
35 }
36
37 pub fn failure(message: impl Into<String>) -> Self {
38 Self {
39 success: false,
40 message: message.into(),
41 }
42 }
43}
44
45pub trait ProcessRunner {
47 fn kill_process(&self, process_name: &str) -> ProcessResult;
48 fn start_process(&self, process_name: &str) -> ProcessResult;
49 fn sleep_ms(&self, ms: u64);
50}
51
52pub struct SystemProcessRunner;
54
55impl ProcessRunner for SystemProcessRunner {
56 fn kill_process(&self, process_name: &str) -> ProcessResult {
57 let result = Command::new("taskkill")
58 .args(["/F", "/IM", process_name])
59 .output();
60
61 match result {
62 Ok(output) => {
63 if output.status.success() {
64 ProcessResult::success(format!("Successfully terminated {}", process_name))
65 } else {
66 let stderr = String::from_utf8_lossy(&output.stderr);
67 ProcessResult::failure(format!(
68 "Failed to terminate {}: {}",
69 process_name, stderr
70 ))
71 }
72 }
73 Err(e) => ProcessResult::failure(format!("Error executing taskkill: {}", e)),
74 }
75 }
76
77 fn start_process(&self, process_name: &str) -> ProcessResult {
78 let result = Command::new(process_name).spawn();
79
80 match result {
81 Ok(_) => ProcessResult::success(format!("Successfully started {}", process_name)),
82 Err(e) => ProcessResult::failure(format!("Error starting {}: {}", process_name, e)),
83 }
84 }
85
86 fn sleep_ms(&self, ms: u64) {
87 std::thread::sleep(std::time::Duration::from_millis(ms));
88 }
89}
90
91pub struct ExplorerManager<R: ProcessRunner> {
93 pub runner: R,
94 pub restart_delay_ms: u64,
95}
96
97impl<R: ProcessRunner> ExplorerManager<R> {
98 pub fn new(runner: R) -> Self {
99 Self {
100 runner,
101 restart_delay_ms: RESTART_DELAY_MS,
102 }
103 }
104
105 pub fn with_restart_delay(mut self, delay_ms: u64) -> Self {
106 self.restart_delay_ms = delay_ms;
107 self
108 }
109
110 pub fn kill(&self) -> bool {
112 println!("{}", "Terminating explorer.exe...".yellow());
113 let result = self.runner.kill_process("explorer.exe");
114
115 if result.success {
116 println!("{}", result.message.green());
117 } else {
118 eprintln!("{}", result.message.red());
119 }
120
121 result.success
122 }
123
124 pub fn start(&self) -> bool {
126 println!("{}", "Starting explorer.exe...".yellow());
127 let result = self.runner.start_process("explorer.exe");
128
129 if result.success {
130 println!("{}", result.message.green());
131 } else {
132 eprintln!("{}", result.message.red());
133 }
134
135 result.success
136 }
137
138 pub fn restart(&self) -> bool {
140 println!("{}", "Restarting explorer.exe...".cyan().bold());
141
142 if !self.kill() {
143 return false;
144 }
145
146 self.runner.sleep_ms(self.restart_delay_ms);
148
149 if !self.start() {
150 return false;
151 }
152
153 println!("{}", "Explorer.exe restarted successfully!".green().bold());
154 true
155 }
156
157 pub fn kill_silent(&self) -> ProcessResult {
159 self.runner.kill_process("explorer.exe")
160 }
161
162 pub fn start_silent(&self) -> ProcessResult {
164 self.runner.start_process("explorer.exe")
165 }
166
167 pub fn restart_silent(&self) -> ProcessResult {
169 let kill_result = self.runner.kill_process("explorer.exe");
170 if !kill_result.success {
171 return kill_result;
172 }
173
174 self.runner.sleep_ms(self.restart_delay_ms);
175
176 let start_result = self.runner.start_process("explorer.exe");
177 if !start_result.success {
178 return start_result;
179 }
180
181 ProcessResult::success("Explorer.exe restarted successfully")
182 }
183}
184
185pub fn is_windows() -> bool {
187 cfg!(target_os = "windows")
188}
189
190pub fn check_platform() -> Result<(), String> {
192 if !is_windows() {
193 Err(format!(
194 "stuckbar is a Windows-only tool.\n\
195 Current platform '{}' is not supported.\n\
196 This tool restarts explorer.exe which only exists on Windows.",
197 std::env::consts::OS
198 ))
199 } else {
200 Ok(())
201 }
202}
203
204#[cfg(feature = "mcp")]
205pub mod mcp;
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use std::cell::RefCell;
211
212 pub struct MockProcessRunner {
214 kill_results: RefCell<Vec<ProcessResult>>,
215 start_results: RefCell<Vec<ProcessResult>>,
216 sleep_calls: RefCell<Vec<u64>>,
217 }
218
219 impl MockProcessRunner {
220 pub fn new() -> Self {
221 Self {
222 kill_results: RefCell::new(Vec::new()),
223 start_results: RefCell::new(Vec::new()),
224 sleep_calls: RefCell::new(Vec::new()),
225 }
226 }
227
228 pub fn with_kill_result(self, result: ProcessResult) -> Self {
229 self.kill_results.borrow_mut().push(result);
230 self
231 }
232
233 pub fn with_start_result(self, result: ProcessResult) -> Self {
234 self.start_results.borrow_mut().push(result);
235 self
236 }
237
238 pub fn get_sleep_calls(&self) -> Vec<u64> {
239 self.sleep_calls.borrow().clone()
240 }
241 }
242
243 impl Default for MockProcessRunner {
244 fn default() -> Self {
245 Self::new()
246 }
247 }
248
249 impl ProcessRunner for MockProcessRunner {
250 fn kill_process(&self, _process_name: &str) -> ProcessResult {
251 self.kill_results
252 .borrow_mut()
253 .pop()
254 .unwrap_or_else(|| ProcessResult::failure("No mock result configured"))
255 }
256
257 fn start_process(&self, _process_name: &str) -> ProcessResult {
258 self.start_results
259 .borrow_mut()
260 .pop()
261 .unwrap_or_else(|| ProcessResult::failure("No mock result configured"))
262 }
263
264 fn sleep_ms(&self, ms: u64) {
265 self.sleep_calls.borrow_mut().push(ms);
266 }
267 }
268
269 #[test]
271 fn test_process_result_success() {
272 let result = ProcessResult::success("test message");
273 assert!(result.success);
274 assert_eq!(result.message, "test message");
275 }
276
277 #[test]
278 fn test_process_result_failure() {
279 let result = ProcessResult::failure("error message");
280 assert!(!result.success);
281 assert_eq!(result.message, "error message");
282 }
283
284 #[test]
285 fn test_process_result_clone() {
286 let result = ProcessResult::success("test");
287 let cloned = result.clone();
288 assert_eq!(result, cloned);
289 }
290
291 #[test]
293 fn test_kill_success() {
294 let runner = MockProcessRunner::new().with_kill_result(ProcessResult::success("Killed"));
295 let manager = ExplorerManager::new(runner);
296
297 assert!(manager.kill());
298 }
299
300 #[test]
301 fn test_kill_failure() {
302 let runner =
303 MockProcessRunner::new().with_kill_result(ProcessResult::failure("Failed to kill"));
304 let manager = ExplorerManager::new(runner);
305
306 assert!(!manager.kill());
307 }
308
309 #[test]
311 fn test_start_success() {
312 let runner = MockProcessRunner::new().with_start_result(ProcessResult::success("Started"));
313 let manager = ExplorerManager::new(runner);
314
315 assert!(manager.start());
316 }
317
318 #[test]
319 fn test_start_failure() {
320 let runner =
321 MockProcessRunner::new().with_start_result(ProcessResult::failure("Failed to start"));
322 let manager = ExplorerManager::new(runner);
323
324 assert!(!manager.start());
325 }
326
327 #[test]
329 fn test_restart_success() {
330 let runner = MockProcessRunner::new()
331 .with_kill_result(ProcessResult::success("Killed"))
332 .with_start_result(ProcessResult::success("Started"));
333 let manager = ExplorerManager::new(runner).with_restart_delay(100);
334
335 assert!(manager.restart());
336 }
337
338 #[test]
339 fn test_restart_kill_fails() {
340 let runner =
341 MockProcessRunner::new().with_kill_result(ProcessResult::failure("Failed to kill"));
342 let manager = ExplorerManager::new(runner);
343
344 assert!(!manager.restart());
345 }
346
347 #[test]
348 fn test_restart_start_fails() {
349 let runner = MockProcessRunner::new()
350 .with_kill_result(ProcessResult::success("Killed"))
351 .with_start_result(ProcessResult::failure("Failed to start"));
352 let manager = ExplorerManager::new(runner);
353
354 assert!(!manager.restart());
355 }
356
357 #[test]
358 fn test_restart_sleeps_between_operations() {
359 let runner = MockProcessRunner::new()
360 .with_kill_result(ProcessResult::success("Killed"))
361 .with_start_result(ProcessResult::success("Started"));
362 let manager = ExplorerManager::new(runner).with_restart_delay(250);
363
364 manager.restart();
365
366 let sleep_calls = &manager.runner.get_sleep_calls();
367 assert_eq!(sleep_calls.len(), 1);
368 assert_eq!(sleep_calls[0], 250);
369 }
370
371 #[test]
372 fn test_restart_uses_default_delay() {
373 let runner = MockProcessRunner::new()
374 .with_kill_result(ProcessResult::success("Killed"))
375 .with_start_result(ProcessResult::success("Started"));
376 let manager = ExplorerManager::new(runner);
377
378 assert_eq!(manager.restart_delay_ms, RESTART_DELAY_MS);
379 }
380
381 #[test]
383 fn test_explorer_manager_with_restart_delay() {
384 let runner = MockProcessRunner::new();
385 let manager = ExplorerManager::new(runner).with_restart_delay(1000);
386 assert_eq!(manager.restart_delay_ms, 1000);
387 }
388
389 #[test]
391 fn test_kill_silent_success() {
392 let runner = MockProcessRunner::new().with_kill_result(ProcessResult::success("Killed"));
393 let manager = ExplorerManager::new(runner);
394
395 let result = manager.kill_silent();
396 assert!(result.success);
397 }
398
399 #[test]
400 fn test_kill_silent_failure() {
401 let runner = MockProcessRunner::new().with_kill_result(ProcessResult::failure("Error"));
402 let manager = ExplorerManager::new(runner);
403
404 let result = manager.kill_silent();
405 assert!(!result.success);
406 }
407
408 #[test]
409 fn test_start_silent_success() {
410 let runner = MockProcessRunner::new().with_start_result(ProcessResult::success("Started"));
411 let manager = ExplorerManager::new(runner);
412
413 let result = manager.start_silent();
414 assert!(result.success);
415 }
416
417 #[test]
418 fn test_start_silent_failure() {
419 let runner = MockProcessRunner::new().with_start_result(ProcessResult::failure("Error"));
420 let manager = ExplorerManager::new(runner);
421
422 let result = manager.start_silent();
423 assert!(!result.success);
424 }
425
426 #[test]
427 fn test_restart_silent_success() {
428 let runner = MockProcessRunner::new()
429 .with_kill_result(ProcessResult::success("Killed"))
430 .with_start_result(ProcessResult::success("Started"));
431 let manager = ExplorerManager::new(runner);
432
433 let result = manager.restart_silent();
434 assert!(result.success);
435 assert_eq!(result.message, "Explorer.exe restarted successfully");
436 }
437
438 #[test]
439 fn test_restart_silent_kill_fails() {
440 let runner =
441 MockProcessRunner::new().with_kill_result(ProcessResult::failure("Kill failed"));
442 let manager = ExplorerManager::new(runner);
443
444 let result = manager.restart_silent();
445 assert!(!result.success);
446 assert_eq!(result.message, "Kill failed");
447 }
448
449 #[test]
450 fn test_restart_silent_start_fails() {
451 let runner = MockProcessRunner::new()
452 .with_kill_result(ProcessResult::success("Killed"))
453 .with_start_result(ProcessResult::failure("Start failed"));
454 let manager = ExplorerManager::new(runner);
455
456 let result = manager.restart_silent();
457 assert!(!result.success);
458 assert_eq!(result.message, "Start failed");
459 }
460
461 #[test]
463 fn test_is_windows() {
464 let result = is_windows();
466 #[cfg(target_os = "windows")]
467 assert!(result);
468 #[cfg(not(target_os = "windows"))]
469 assert!(!result);
470 }
471
472 #[test]
473 fn test_check_platform() {
474 let result = check_platform();
475 #[cfg(target_os = "windows")]
476 assert!(result.is_ok());
477 #[cfg(not(target_os = "windows"))]
478 {
479 assert!(result.is_err());
480 let err = result.unwrap_err();
481 assert!(err.contains("Windows-only"));
482 assert!(err.contains(std::env::consts::OS));
483 }
484 }
485}