cargo_e/
e_processmanager.rs

1// src/e_process_manager.rs
2
3use crate::e_cargocommand_ext::{CargoProcessHandle, CargoProcessResult};
4use crate::e_command_builder::TerminalError;
5use crate::e_target::CargoTarget;
6use crate::Cli;
7use chrono::Local;
8use std::collections::HashMap;
9use std::process::ExitStatus;
10use std::sync::atomic::AtomicUsize;
11use std::sync::mpsc::{self, Receiver, Sender};
12use std::sync::{Arc, Mutex};
13use std::thread::{self, sleep};
14use std::time::{Duration, SystemTime};
15use sysinfo::System;
16// use std::io::Write;
17#[cfg(feature = "tui")]
18use crossterm::{
19    cursor, execute,
20    terminal::{Clear, ClearType},
21};
22use std::io::{self, Write};
23use std::sync::atomic::Ordering;
24#[cfg(unix)]
25use {
26    nix::sys::signal::{kill as nix_kill, Signal},
27    nix::unistd::Pid,
28    std::os::unix::process::ExitStatusExt,
29};
30impl ProcessObserver for ProcessManager {
31    fn on_spawn(&self, pid: u32, handle: CargoProcessHandle) {
32        self.processes
33            .lock()
34            .unwrap()
35            .insert(pid, Arc::new(Mutex::new(handle)));
36    }
37    // let pid = handle.lock().unwrap().pid;
38    // self.processes.lock().unwrap().insert(pid, handle);
39    // Ok(())
40}
41
42// #[cfg(feature = "uses_async")]
43// use tokio::sync::Notify;
44
45// pub static PROCESS_MANAGER: Lazy<ProcessManager> = Lazy::new(ProcessManager::new);
46
47pub trait ProcessObserver: Send + Sync + 'static {
48    fn on_spawn(&self, pid: u32, handle: CargoProcessHandle);
49}
50
51pub struct ProcessManager {
52    signalled_count: AtomicUsize,
53    signal_tx: Sender<()>,
54    processes: Mutex<HashMap<u32, Arc<Mutex<CargoProcessHandle>>>>,
55    // #[cfg(feature = "uses_async")]
56    // notifier: Notify,
57    results: Mutex<Vec<CargoProcessResult>>,
58}
59
60impl ProcessManager {
61    pub fn new(_cli: &Cli) -> Arc<Self> {
62        let (tx, rx) = mpsc::channel();
63        let manager = Arc::new(Self {
64            signalled_count: AtomicUsize::new(0),
65            signal_tx: tx.clone(),
66            processes: Mutex::new(HashMap::new()),
67            results: Mutex::new(Vec::new()),
68        });
69        ProcessManager::install_handler(Arc::clone(&manager), rx);
70        manager
71    }
72
73    pub fn reset_signalled(&self) {
74        self.signalled_count.store(0, Ordering::SeqCst);
75    }
76    pub fn has_signalled(&self) -> usize {
77        self.signalled_count.load(Ordering::SeqCst)
78    }
79
80    fn install_handler(self_: Arc<Self>, rx: Receiver<()>) {
81        match ctrlc::set_handler({
82            let tx = self_.signal_tx.clone();
83            move || {
84                let _ = tx.send(());
85            }
86        }) {
87            Ok(_) => {
88                thread::spawn(move || {
89                    while rx.recv().is_ok() {
90                        self_.signalled_count.fetch_add(1, Ordering::SeqCst);
91                        println!(
92                            "ctrlc> signal received.  {}",
93                            self_.signalled_count.load(Ordering::SeqCst)
94                        );
95                        self_.handle_signal();
96                    }
97                });
98            }
99            Err(e) => {
100                eprintln!("ctrlc> Failed to install Ctrl+C handler: {}", e);
101                return;
102            }
103        }
104    }
105
106    fn handle_signal(&self) {
107        println!("ctrlc>");
108        let mut processes = self.processes.lock().unwrap();
109        for (pid, handle) in processes.iter() {
110            println!("ctrlc> Terminating process with PID: {}", pid);
111            if let Ok(mut h) = handle.lock() {
112                let _ = h.kill();
113                let final_diagnostics = {
114                    let diag_lock = h.diagnostics.lock().unwrap();
115                    diag_lock.clone()
116                };
117                h.result.diagnostics = final_diagnostics.clone();
118
119                // Ensure `es` is properly defined or assigned
120                if let Some(exit_status) = h.child.try_wait().ok().flatten() {
121                    h.result.exit_status = Some(exit_status);
122                }
123
124                h.result.end_time = Some(SystemTime::now());
125                if let (Some(start), Some(end)) = (h.result.start_time, h.result.end_time) {
126                    h.result.elapsed_time = Some(end.duration_since(start).unwrap_or_default());
127                }
128                self.record_result(h.result.clone());
129            }
130        }
131        processes.clear();
132    }
133
134    /// Updates the status line in the terminal.
135    /// When `raw_mode` is true, it uses crossterm commands to clear the current line.
136    /// Otherwise, it uses the carriage return (`\r`) trick.
137    pub fn update_status_line(output: &str, raw_mode: bool) -> io::Result<()> {
138        let mut stdout = io::stdout();
139        if raw_mode {
140            // Move cursor to beginning and clear the current line.
141            #[cfg(feature = "tui")]
142            {
143                execute!(
144                    stdout,
145                    cursor::MoveToColumn(0),
146                    Clear(ClearType::CurrentLine)
147                )?;
148                print!("{}", output);
149            }
150            #[cfg(not(feature = "tui"))]
151            print!("\r{}", output);
152        } else {
153            // In non-raw mode, the \r trick can work.
154            print!("\r{}", output);
155        }
156        stdout.flush()
157    }
158
159    pub fn register(&self, handle: CargoProcessHandle) -> u32 {
160        let pid = handle.pid;
161        self.processes
162            .lock()
163            .unwrap()
164            .insert(pid, Arc::new(Mutex::new(handle)));
165
166        // #[cfg(feature = "uses_async")]
167        // self.notifier.notify_waiters();
168
169        pid
170    }
171
172    pub fn take(&self, pid: u32) -> Option<Arc<Mutex<CargoProcessHandle>>> {
173        self.processes.lock().unwrap().remove(&pid)
174    }
175
176    pub fn remove(&self, pid: u32) {
177        if let Some(handle_arc) = self.processes.lock().unwrap().remove(&pid) {
178            let mut h = handle_arc.lock().unwrap();
179            let final_diagnostics = {
180                let diag_lock = h.diagnostics.lock().unwrap();
181                diag_lock.clone()
182            };
183            h.result.diagnostics = final_diagnostics.clone();
184
185            // Ensure `es` is properly defined or assigned
186            if let Some(exit_status) = h.child.try_wait().ok().flatten() {
187                h.result.exit_status = Some(exit_status);
188            }
189
190            h.result.end_time = Some(SystemTime::now());
191            if let (Some(start), Some(end)) = (h.result.start_time, h.result.end_time) {
192                h.result.elapsed_time = Some(end.duration_since(start).unwrap_or_default());
193            }
194            self.record_result(h.result.clone());
195            drop(h);
196            // This was the only Arc reference, so dropping it here will run CargoProcessHandle::drop()
197            drop(handle_arc);
198        }
199    }
200
201    pub fn try_wait(&self, pid: u32) -> anyhow::Result<Option<ExitStatus>> {
202        // 1. Lock the processes map just long enough to clone the Arc.
203        let handle_arc = {
204            let processes = self.processes.lock().unwrap();
205            // Clone the Arc to keep the handle in the map while getting your own reference.
206            processes
207                .get(&pid)
208                .ok_or_else(|| anyhow::anyhow!("Process handle with PID {} not found", pid))?
209                .clone()
210        };
211
212        // 2. Lock the individual process handle to perform try_wait.
213        let mut handle = handle_arc.lock().unwrap();
214        // Here, try_wait returns a Result<Option<ExitStatus>, std::io::Error>.
215        // The '?' operator will convert any std::io::Error to anyhow::Error automatically.
216        let status = handle.child.try_wait()?;
217
218        // Return the exit status (or None) wrapped in Ok.
219        Ok(status)
220    }
221
222    pub fn get(&self, pid: u32) -> Option<Arc<Mutex<CargoProcessHandle>>> {
223        self.processes.lock().unwrap().get(&pid).cloned()
224    }
225
226    pub fn list(&self) -> Vec<u32> {
227        self.processes.lock().unwrap().keys().cloned().collect()
228    }
229
230    pub fn status(&self) {
231        let processes = self.processes.lock().unwrap();
232        if processes.is_empty() {
233            println!("No active cargo processes.");
234        } else {
235            println!("Active processes:");
236            for pid in processes.keys() {
237                println!(" - PID: {}", pid);
238            }
239        }
240    }
241
242    // /// Attempts to kill the process corresponding to the provided PID.
243    // /// Returns Ok(true) if the process was found and successfully killed (or had already exited),
244    // /// Ok(false) if the process was not found or did not exit within the maximum attempts,
245    // /// or an error if something went wrong.
246    // pub fn kill_by_pid(&self, pid: u32) -> anyhow::Result<bool> {
247    //     // Retrieve a clone of the handle (Arc) for the given PID without removing it.
248    //     let handle = {
249    //         let processes = self.processes.lock().unwrap();
250    //         processes.get(&pid).cloned()
251    //     };
252
253    //     if let Some(handle) = handle {
254    //         eprintln!("Attempting to kill PID: {}", pid);
255
256    //         let max_attempts = 3;
257    //         let mut attempts = 0;
258    //         let mut process_exited = false;
259
260    //         loop {
261    //             // Lock the process handle for this iteration.
262    //             if let Ok(mut h) = handle.lock() {
263    //                 // Check if the process has already exited.
264    //                 match h.child.try_wait() {
265    //                     Ok(Some(status)) => {
266    //                         eprintln!("Process {} already exited with status: {:?}", pid, status);
267    //                         process_exited = true;
268    //                         break;
269    //                     }
270    //                     Ok(None) => {
271    //                         // Process is still running.
272    //                         if attempts == 0 {
273    //                             #[cfg(not(target_os = "windows"))] {
274    //                                 eprintln!("Sending initial Ctrl+C signal to PID: {}", pid);
275    //                                 crate::e_runall::send_ctrl_c(&mut h.child)?;
276    //                             }
277    //                             #[cfg(target_os = "windows")] {
278    //                                 eprintln!("Sending initial kill signal to PID: {}", pid);
279    //                             }
280    //                         } else {
281    //                             eprintln!("Attempt {}: Forcing kill for PID: {}", attempts, pid);
282    //                         }
283    //                         // Attempt to kill the process.
284    //                         if let Err(e) = h.kill() {
285    //                             eprintln!("Failed to send kill signal to PID {}: {}", pid, e);
286    //                         }
287    //                         // Mark that an exit was requested.
288    //                         h.requested_exit = true;
289    //                     }
290    //                     Err(e) => {
291    //                         eprintln!("Error checking exit status for PID {}: {}", pid, e);
292    //                         break;
293    //                     }
294    //                 }
295    //             } else {
296    //                 eprintln!("Could not lock process handle for PID {}", pid);
297    //                 break;
298    //             }
299
300    //             attempts += 1;
301    //             // Allow some time for the process to exit.
302    //             sleep(Duration::from_millis(2000));
303
304    //             // Re-check if the process has exited.
305    //             if let Ok(mut h) = handle.lock() {
306    //                 match h.child.try_wait() {
307    //                     Ok(Some(status)) => {
308    //                         eprintln!("Process {} exited with status: {:?}", pid, status);
309    //                         process_exited = true;
310    //                         break;
311    //                     }
312    //                     Ok(None) => {
313    //                         eprintln!("Process {} still running after attempt {}.", pid, attempts);
314    //                     }
315    //                     Err(e) => {
316    //                         eprintln!("Error rechecking process {}: {}", pid, e);
317    //                         break;
318    //                     }
319    //                 }
320    //             }
321
322    //             if attempts >= max_attempts {
323    //                 eprintln!("Maximum kill attempts reached for PID {}.", pid);
324    //                 break;
325    //             }
326    //         }
327
328    //         // If the process exited, remove it from the map.
329    //         if process_exited {
330    //             let mut processes = self.processes.lock().unwrap();
331    //             processes.remove(&pid);
332    //             eprintln!("Process {} removed from map after exit.", pid);
333    //         } else {
334    //             eprintln!(
335    //                 "Process {} did not exit after {} attempts; it remains in the map for future handling.",
336    //                 pid, attempts
337    //             );
338    //         }
339    //         Ok(process_exited)
340    //     } else {
341    //         eprintln!("Process handle with PID {} not found.", pid);
342    //         Ok(false)
343    //     }
344    // }
345
346    /// Attempts to kill the process corresponding to the provided PID.
347    /// Returns Ok(true) if the process was found and exited (even via signal),
348    /// Ok(false) if the process wasn’t found or didn’t exit after trying
349    /// all signals (in which case we drop the handle), or Err if something went wrong.
350    pub fn kill_by_pid(&self, pid: u32) -> anyhow::Result<bool> {
351        // Grab the handle, if any.
352        let handle_opt = {
353            let procs = self.processes.lock().unwrap();
354            procs.get(&pid).cloned()
355        };
356
357        if let Some(handle) = handle_opt {
358            eprintln!("Attempting to kill PID: {}", pid);
359            #[cfg(unix)]
360            let signals = [
361                Signal::SIGHUP,
362                Signal::SIGINT,
363                Signal::SIGQUIT,
364                Signal::SIGABRT,
365                Signal::SIGKILL,
366                Signal::SIGALRM,
367                Signal::SIGTERM,
368            ];
369            #[cfg(unix)]
370            let max_attempts = signals.len();
371
372            #[cfg(windows)]
373            let max_attempts = 3; // arbitrary, since Child::kill() is always SIGKILL
374
375            let mut attempts = 0;
376            let mut did_exit = false;
377
378            while attempts < max_attempts {
379                // 1) Check status
380                if let Ok(mut h) = handle.lock() {
381                    match h.child.try_wait() {
382                        Ok(Some(status)) => {
383                            // Child has exited—report how.
384                            #[cfg(unix)]
385                            {
386                                if let Some(sig) = status.signal() {
387                                    eprintln!("Process {} terminated by signal {}", pid, sig);
388                                } else if let Some(code) = status.code() {
389                                    eprintln!("Process {} exited with code {}", pid, code);
390                                } else {
391                                    eprintln!(
392                                        "Process {} exited with unknown status: {:?}",
393                                        pid, status
394                                    );
395                                }
396                            }
397                            #[cfg(not(unix))]
398                            {
399                                if let Some(code) = status.code() {
400                                    eprintln!("Process {} exited with code {}", pid, code);
401                                } else {
402                                    eprintln!(
403                                        "Process {} exited with unknown status: {:?}",
404                                        pid, status
405                                    );
406                                }
407                            }
408                            did_exit = true;
409                            break;
410                        }
411                        Ok(None) => {
412                            // Still running → send the next signal
413                            #[cfg(unix)]
414                            {
415                                let sig = signals[attempts];
416                                eprintln!(
417                                    "Attempt {}: sending {:?} to PID {}",
418                                    attempts + 1,
419                                    sig,
420                                    pid
421                                );
422                                nix_kill(Pid::from_raw(pid as i32), sig)?;
423                            }
424                            #[cfg(windows)]
425                            {
426                                // // Remove the handle so it drops (and on Windows that will kill if still alive)
427                                // {
428                                //     let mut procs = self.processes.lock().unwrap();
429                                //     procs.remove(&pid);
430                                // }
431
432                                eprintln!("Attempt {}: killing PID {}", attempts + 1, pid);
433                                if let Err(e) = h.child.kill() {
434                                    eprintln!("Failed to kill PID {}: {}", pid, e);
435                                }
436                                _ = std::process::Command::new("taskkill")
437                                    .args(["/F", "/PID", &pid.to_string()])
438                                    .spawn();
439                            }
440                            h.requested_exit = true;
441                        }
442                        Err(e) => {
443                            eprintln!("Error checking status for PID {}: {}", pid, e);
444                            break;
445                        }
446                    }
447                } else {
448                    eprintln!("Could not lock handle for PID {}", pid);
449                    break;
450                }
451
452                attempts += 1;
453                if did_exit {
454                    break;
455                }
456
457                // Give it a moment before retrying
458                thread::sleep(Duration::from_secs(2));
459            }
460            // Remove the handle so it drops (and on Windows that will kill if still alive)
461            {
462                let mut procs = self.processes.lock().unwrap();
463                if let Some(handle_arc) = procs.remove(&pid) {
464                    let mut handle = handle_arc.lock().unwrap();
465                    let final_diagnostics = {
466                        let diag_lock = handle.diagnostics.lock().unwrap();
467                        diag_lock.clone()
468                    };
469                    handle.result.diagnostics = final_diagnostics.clone();
470
471                    // Ensure `es` is properly defined or assigned
472                    if let Some(exit_status) = handle.child.try_wait().ok().flatten() {
473                        handle.result.exit_status = Some(exit_status);
474                    }
475
476                    handle.result.end_time = Some(SystemTime::now());
477                    if let (Some(start), Some(end)) =
478                        (handle.result.start_time, handle.result.end_time)
479                    {
480                        handle.result.elapsed_time =
481                            Some(end.duration_since(start).unwrap_or_default());
482                    }
483                    self.record_result(handle.result.clone());
484                } else {
485                    eprintln!("No process found with PID: {}", pid);
486                }
487            }
488
489            if did_exit {
490                eprintln!("Process {} removed from map after exit.", pid);
491            } else {
492                eprintln!(
493                    "Dropped handle for PID {} after {} attempts (process may still be running).",
494                    pid, attempts
495                );
496            }
497
498            Ok(did_exit)
499        } else {
500            eprintln!("Process handle with PID {} not found.", pid);
501            Ok(false)
502        }
503    }
504
505    //     /// Attempts to kill the process corresponding to the provided PID.
506    // /// Returns Ok(true) if the process was found and successfully killed
507    // /// (or had already exited), Ok(false) if the process was not found
508    // /// or did not exit within the maximum attempts (in which case we drop
509    // /// the handle), or an error if something went wrong.
510    // pub fn kill_by_pid(&self, pid: u32) -> anyhow::Result<bool> {
511    //     // Grab a clone of the Arc<Mutex<ProcessHandle>> if it exists
512    //     let handle_opt = {
513    //         let processes = self.processes.lock().unwrap();
514    //         processes.get(&pid).cloned()
515    //     };
516
517    //     if let Some(handle) = handle_opt {
518    //         eprintln!("Attempting to kill PID: {}", pid);
519
520    //         let max_attempts = 3;
521    //         let mut attempts = 0;
522    //         let mut process_exited = false;
523
524    //         loop {
525    //             // 1) Check status / send signal
526    //             if let Ok(mut h) = handle.lock() {
527    //                 match h.child.try_wait() {
528    //                     Ok(Some(status)) => {
529    //                         // Already exited
530    //                         eprintln!(
531    //                             "Process {} already exited with status: {:?}",
532    //                             pid, status
533    //                         );
534    //                         process_exited = true;
535    //                         break;
536    //                     }
537    //                     Ok(None) => {
538    //                         // Still running → send signal
539    //                         if attempts == 0 {
540    //                             #[cfg(not(target_os = "windows"))]
541    //                             {
542    //                                 eprintln!("Sending initial Ctrl+C to PID: {}", pid);
543    //                                 crate::e_runall::send_ctrl_c(&mut h.child)?;
544    //                             }
545    //                             #[cfg(target_os = "windows")]
546    //                             {
547    //                                 eprintln!("Sending initial kill to PID: {}", pid);
548    //                             }
549    //                         } else {
550    //                             eprintln!("Attempt {}: Forcing kill for PID: {}", attempts, pid);
551    //                         }
552
553    //                         if let Err(e) = h.kill() {
554    //                             eprintln!("Failed to send kill to PID {}: {}", pid, e);
555    //                         }
556    //                         h.requested_exit = true;
557    //                     }
558    //                     Err(e) => {
559    //                         eprintln!("Error checking status for PID {}: {}", pid, e);
560    //                         break;
561    //                     }
562    //                 }
563    //             } else {
564    //                 eprintln!("Could not lock handle for PID {}", pid);
565    //                 break;
566    //             }
567
568    //             attempts += 1;
569    //             if attempts >= max_attempts {
570    //                 eprintln!("Maximum kill attempts reached for PID {}. Dropping handle.", pid);
571    //                 break;
572    //             }
573
574    //             // 2) Wait a bit before re-checking
575    //             thread::sleep(Duration::from_millis(2_000));
576
577    //             // 3) Re-check exit status
578    //             if let Ok(mut h) = handle.lock() {
579    //                 match h.child.try_wait() {
580    //                     Ok(Some(status)) => {
581    //                         eprintln!("Process {} exited with status: {:?}", pid, status);
582    //                         process_exited = true;
583    //                         break;
584    //                     }
585    //                     Ok(None) => {
586    //                         eprintln!(
587    //                             "Process {} still running after attempt {}.",
588    //                             pid, attempts
589    //                         );
590    //                     }
591    //                     Err(e) => {
592    //                         eprintln!("Error rechecking process {}: {}", pid, e);
593    //                         break;
594    //                     }
595    //                 }
596    //             }
597    //         }
598
599    //         // Remove the handle (dropping it) whether or not the process exited
600    //         {
601    //             let mut processes = self.processes.lock().unwrap();
602    //             processes.remove(&pid);
603    //         }
604
605    //         if process_exited {
606    //             eprintln!("Process {} removed from map after exit.", pid);
607    //         } else {
608    //             eprintln!(
609    //                 "Dropped handle for PID {} after {} attempts (process may still be running).",
610    //                 pid, attempts
611    //             );
612    //         }
613
614    //         Ok(process_exited)
615    //     } else {
616    //         eprintln!("Process handle with PID {} not found.", pid);
617    //         Ok(false)
618    //     }
619    // }
620
621    /// Attempts to kill one process.
622    /// Returns Ok(true) if a process was found and killed, Ok(false) if none found,
623    /// or an error if something went wrong.
624    pub fn kill_one(&self) -> anyhow::Result<bool> {
625        // First, lock the map briefly to pick one process handle.
626        let maybe_entry = {
627            let processes = self.processes.lock().unwrap();
628            // Clone the Arc so that we don’t take ownership from the map.
629            processes
630                .iter()
631                .next()
632                .map(|(&pid, handle)| (pid, handle.clone()))
633        };
634
635        if let Some((pid, handle)) = maybe_entry {
636            eprintln!("Attempting to kill PID: {}", pid);
637
638            // We'll attempt to kill the process up to `max_attempts` times.
639            let max_attempts = 3;
640            let mut attempts = 0;
641            let mut process_exited = false;
642
643            loop {
644                // Lock the process handle for this iteration.
645                if let Ok(mut h) = handle.lock() {
646                    // Check if the process has already exited.
647                    match h.child.try_wait() {
648                        Ok(Some(status)) => {
649                            eprintln!("Process {} already exited with status: {:?}", pid, status);
650                            process_exited = true;
651                            sleep(Duration::from_millis(3_000));
652                            break;
653                        }
654                        Ok(None) => {
655                            // Process is still running. On the first attempt, or forcefully on later attempts.
656                            if attempts == 0 {
657                                eprintln!("Sending initial kill signal to PID: {}", pid);
658                            } else {
659                                eprintln!("Attempt {}: Forcing kill for PID: {}", attempts, pid);
660                            }
661                            sleep(Duration::from_millis(3_000));
662                            // Try to kill the process. Handle errors by printing a debug message.
663                            if let Err(e) = h.kill() {
664                                eprintln!("Failed to send kill signal to PID {}: {}", pid, e);
665                                sleep(Duration::from_millis(3_000));
666                            }
667                        }
668                        Err(e) => {
669                            eprintln!("Error checking exit status for PID {}: {}", pid, e);
670                            sleep(Duration::from_millis(3_000));
671                            break;
672                        }
673                    }
674                } else {
675                    eprintln!("Could not lock process handle for PID {}", pid);
676                    sleep(Duration::from_millis(3_000));
677                    break;
678                }
679
680                attempts += 1;
681                // Allow some time for the process to exit.
682                sleep(Duration::from_millis(3_000));
683
684                // Check again after the sleep.
685                if let Ok(mut h) = handle.lock() {
686                    match h.child.try_wait() {
687                        Ok(Some(status)) => {
688                            eprintln!("Process {} exited with status: {:?}", pid, status);
689                            sleep(Duration::from_millis(3_000));
690                            process_exited = true;
691                            break;
692                        }
693                        Ok(None) => {
694                            eprintln!("Process {} still running after attempt {}.", pid, attempts);
695                            sleep(Duration::from_millis(3_000));
696                        }
697                        Err(e) => {
698                            eprintln!("Error rechecking process {}: {}", pid, e);
699                            sleep(Duration::from_millis(3_000));
700                            break;
701                        }
702                    }
703                }
704
705                if attempts >= max_attempts {
706                    eprintln!("Maximum kill attempts reached for PID {}.", pid);
707                    sleep(Duration::from_millis(3_000));
708                    break;
709                }
710            }
711
712            // 4) In all cases, remove the handle so it drops
713            {
714                let mut ps = self.processes.lock().unwrap();
715                ps.remove(&pid);
716            }
717            if process_exited {
718                eprintln!("Process {} removed from map after exit.", pid);
719            } else {
720                eprintln!(
721                    "Dropped handle for PID {} after {} attempts (process may still be running).",
722                    pid, attempts
723                );
724            }
725            sleep(Duration::from_millis(3_000));
726            Ok(process_exited)
727        } else {
728            println!("No processes to kill.");
729            sleep(Duration::from_millis(3_000));
730            Ok(false)
731        }
732    }
733    // pub fn kill_one(&self) {
734    //     let mut processes = self.processes.lock().unwrap();
735    //     if let Some((&pid, handle)) = processes.iter().next() {
736    //         eprintln!("Killing PID: {}", pid);
737    //         if let Ok(mut h) = handle.lock() {
738    //             let _ = h.kill();
739    //         }
740    //         processes.remove(&pid);
741    //     } else {
742    //         println!("No processes to kill.");
743    //     }
744    // }
745
746    pub fn kill_all(&self) {
747        let mut processes = self.processes.lock().unwrap();
748        for (pid, handle) in processes.drain() {
749            eprintln!("Killing PID: {}", pid);
750            if let Ok(mut h) = handle.lock() {
751                let _ = h.kill();
752            }
753        }
754    }
755
756    // Returns the terminal error for a given PID.
757    pub fn get_terminal_error(&self, pid: u32) -> Option<TerminalError> {
758        // Lock the process map
759        let processes = self.processes.lock().unwrap();
760
761        // Check if the process exists
762        if let Some(handle) = processes.get(&pid) {
763            // Lock the handle to access the terminal error flag
764            let handle = handle.lock().unwrap();
765            // Return the terminal error flag value
766            return Some(handle.terminal_error_flag.lock().unwrap().clone());
767        }
768
769        // If no process is found for the given PID, return None
770        None
771    }
772
773    //     pub fn install_ctrlc_handler(self: Arc<Self>) {
774    //     let manager = Arc::clone(&self);
775    //     ctrlc::set_handler(move || {
776    //         eprintln!("CTRL-C detected. Killing all processes.");
777    //         manager.kill_all();
778    //         std::process::exit(1);
779    //     })
780    //     .expect("Failed to install ctrl-c handler");
781    // }
782
783    /// Wait for the process to finish, show interactive status, then return a result
784    pub fn wait(
785        &self,
786        pid: u32,
787        _duration: Option<Duration>,
788    ) -> anyhow::Result<CargoProcessResult> {
789        // Hide the cursor and ensure it’s restored on exit
790        #[allow(dead_code)]
791        struct CursorHide;
792        impl Drop for CursorHide {
793            fn drop(&mut self) {
794                #[cfg(feature = "tui")]
795                {
796                    let _ = crossterm::execute!(std::io::stdout(), crossterm::cursor::Show);
797                }
798            }
799        }
800        #[cfg(feature = "tui")]
801        let _cursor_hide = {
802            let mut out = std::io::stdout();
803            crossterm::execute!(out, crossterm::cursor::Hide)?;
804            CursorHide
805        };
806
807        // 1. Remove the handle from the map
808        let handle_arc = {
809            let mut map = self.processes.lock().unwrap();
810            map.remove(&pid).ok_or_else(|| {
811                let result = CargoProcessResult {
812                    target_name: String::new(), // Placeholder, should be set properly in actual use
813                    cmd: String::new(),         // Placeholder, should be set properly in actual use
814                    args: Vec::new(),           // Placeholder, should be set properly in actual use
815                    pid,
816                    exit_status: None,
817                    diagnostics: Vec::new(),
818                    start_time: None,
819                    end_time: Some(SystemTime::now()),
820                    elapsed_time: None,
821                    terminal_error: None, // Placeholder, should be set properly in actual use
822                    build_finished_time: None, // Placeholder, should be set properly in actual use
823                    build_elapsed: None,  // Placeholder, should be set properly in actual use
824                    runtime_elapsed: None, // Placeholder, should be set properly in actual use
825                    stats: crate::e_cargocommand_ext::CargoStats::default(), // Provide a default instance of CargoStats
826                    build_output_size: 0,   // Default value set to 0
827                    runtime_output_size: 0, // Placeholder, should be set properly in actual use
828                    is_filter: false,       // Placeholder, should be set properly in actual use
829                };
830                self.record_result(result.clone());
831                anyhow::anyhow!("Process handle with PID {} not found", pid)
832            })?
833        };
834
835        // 2. Unwrap Arc<Mutex<...>> to get the handle
836        let mut handle = Arc::try_unwrap(handle_arc)
837            .map_err(|_| anyhow::anyhow!("Process handle for PID {} still shared", pid))?
838            .into_inner()
839            .unwrap();
840
841        // 3. Interactive polling loop
842        let mut system = if handle.is_filter {
843            Some(System::new_all())
844        } else {
845            None
846        };
847        const POLL: Duration = Duration::from_secs(1);
848        let mut loop_cnter = 0;
849        loop {
850            loop_cnter += 1;
851            if handle.is_filter && loop_cnter % 2 == 0 {
852                if let Some(ref mut sys) = system {
853                    sys.refresh_all();
854                }
855            }
856
857            if handle.is_filter {
858                if let Some(ref sys) = system {
859                    if let Some(process) = sys.process((pid as usize).into()) {
860                        let status = handle.format_status(Some(process));
861                        if !status.is_empty() {
862                            print!("\r{}", status);
863                        }
864                    }
865                }
866                std::io::stdout().flush().unwrap();
867            }
868
869            if let Some(es) = handle.child.try_wait()? {
870                let final_diagnostics = {
871                    let diag_lock = handle.diagnostics.lock().unwrap();
872                    diag_lock.clone()
873                };
874                handle.result.diagnostics = final_diagnostics.clone();
875                handle.result.exit_status = Some(es);
876                handle.result.end_time = Some(SystemTime::now());
877                if let (Some(start), Some(end)) = (handle.result.start_time, handle.result.end_time)
878                {
879                    handle.result.elapsed_time =
880                        Some(end.duration_since(start).unwrap_or_default());
881                }
882                println!(
883                    "\nProcess with PID {} finished {:?} {}",
884                    pid,
885                    es,
886                    handle.result.diagnostics.len()
887                );
888                break;
889            }
890            std::thread::sleep(POLL);
891        }
892
893        if handle.is_filter {
894            // 4. Extract diagnostics out of Arc<Mutex<_>>
895            let diagnostics = Arc::try_unwrap(handle.diagnostics)
896                .map(|m| m.into_inner().unwrap())
897                .unwrap_or_else(|arc| arc.lock().unwrap().clone());
898
899            // 5. Move them into the final result
900            handle.result.diagnostics = diagnostics;
901        }
902        self.record_result(handle.result.clone());
903        Ok(handle.result)
904    }
905
906    pub fn record_result(&self, result: CargoProcessResult) {
907        let mut results = self.results.lock().unwrap();
908        results.push(result);
909    }
910
911    pub fn generate_report(&self, create_gist: bool) {
912        let results = self.results.lock().unwrap();
913        let report = crate::e_reports::generate_markdown_report(&results);
914        if let Err(e) = crate::e_reports::save_report_to_file(&report, "run_report.md") {
915            eprintln!("Failed to save report: {}", e);
916        }
917        if create_gist {
918            crate::e_reports::create_gist(&report, "run_report.md").unwrap_or_else(|e| {
919                eprintln!("Failed to create Gist: {}", e);
920            });
921        }
922    }
923    //     pub fn wait(&self, pid: u32, _duration: Option<Duration>) -> anyhow::Result<CargoProcessResult> {
924    //     // Hide the cursor and ensure it is restored on exit.
925    //     {
926    //         let mut stdout = std::io::stdout();
927    //         crossterm::execute!(stdout, crossterm::cursor::Hide)?;
928    //     }
929
930    //     let mut processes = self.processes.lock().unwrap();
931    //     if let Some(handle) = processes.get_mut(&pid) {
932    //         let mut handle = handle.lock().unwrap();
933    //         let mut system = System::new_all();
934
935    //         // Initialize start_time if not already set.
936    //         let start_time = handle.result.start_time.unwrap_or_else(|| {
937    //             let now = SystemTime::now();
938    //             handle.result.start_time = Some(now);
939    //             now
940    //         });
941
942    //         // Main loop.
943    //         const POLL_DURATION: Duration = Duration::from_secs(1);
944    //         loop {
945    //             system.refresh_all();
946    //             let maybe_system: Option<&System> = if true { Some(&system) } else { None };
947    //             // Get formatted status string.
948    //             let output = handle.format_status(maybe_system);
949    //             print!("\r{}", output);
950    //             std::io::stdout().flush().unwrap();
951
952    //             if let Some(status) = handle.child.try_wait()? {
953    //                 handle.result.exit_status = Some(status);
954    //                 handle.result.end_time = Some(SystemTime::now());
955    //                 println!("\nProcess with PID {} finished", pid);
956    //                 return Ok(handle.result.clone());
957    //             }
958    //             std::thread::sleep(POLL_DURATION);
959    //         }
960    //     } else {
961    //         Err(anyhow::anyhow!("Process handle with PID {} not found", pid))
962    //     }
963    // }
964
965    // pub fn wait(&self, pid: u32, _duration: Option<Duration>) -> anyhow::Result<CargoProcessResult> {
966    //     // Turn off (hide) the cursor.
967    //     {
968    //         let mut stdout = std::io::stdout();
969    //         crossterm::execute!(stdout, crossterm::cursor::Hide)?;
970    //     }
971    //     // Ensure the cursor is shown when we exit.
972    //     let _cursor_guard = CursorGuard;
973
974    //     let mut processes = self.processes.lock().unwrap();
975    //     if let Some(handle) = processes.get_mut(&pid) {
976    //         let mut handle = handle.lock().unwrap();
977    //         let mut system = System::new_all();
978
979    //         // Define the poll duration constant (adjust as needed).
980    //         const POLL_DURATION: Duration = Duration::from_secs(1);
981
982    //         // Initialize start_time if not already set.
983    //         let start_time = handle.result.start_time.unwrap_or_else(|| {
984    //             let now = SystemTime::now();
985    //             handle.result.start_time = Some(now);
986    //             now
987    //         });
988    //         // Format the start time with seconds precision.
989    //         let start_dt: chrono::DateTime<Local> = start_time.into();
990    //         let start_str = start_dt.format("%H:%M:%S").to_string();
991    //         // Use ANSI color for the start time.
992    //         let colored_start = nu_ansi_term::Color::Green.paint(&start_str).to_string();
993    //         // Plain version for spacing calculations.
994    //         let plain_start = start_str;
995
996    //         loop {
997    //             system.refresh_all();
998    //             let now = SystemTime::now();
999    //             let runtime_duration = now.duration_since(start_time).unwrap();
1000    //             let runtime_str = crate::e_fmt::format_duration(runtime_duration);
1001
1002    //             // Use usize conversion with into()
1003    //             if let Some(process) = system.process((pid as usize).into()) {
1004    //                 let cpu_usage = process.cpu_usage();
1005    //                 let mem_kb = process.memory();
1006    //                 let mem_human = if mem_kb >= 1024 {
1007    //                     format!("{:.2} MB", mem_kb as f64 / 1024.0)
1008    //                 } else {
1009    //                     format!("{} KB", mem_kb)
1010    //                 };
1011
1012    //                 let left_display = format!("{} | CPU: {:.2}% | Mem: {}", colored_start, cpu_usage, mem_human);
1013    //                 let left_plain = format!("{} | CPU: {:.2}% | Mem: {}", plain_start, cpu_usage, mem_human);
1014
1015    //                 // Get terminal width with crossterm.
1016    //                 let (cols, _) = crossterm::terminal::size().unwrap_or((80, 20));
1017    //                 let total_width = cols as usize;
1018    //                 // Right side: the elapsed duration, underlined.
1019    //                 let right_display = nu_ansi_term::Style::new()
1020    //                     .reset_before_style()
1021    //                     .underline()
1022    //                     .paint(&runtime_str)
1023    //                     .to_string();
1024    //                 let left_len = left_plain.len();
1025    //                 let right_len = runtime_str.len();
1026    //                 let padding = if total_width > left_len + right_len {
1027    //                     total_width - left_len - right_len
1028    //                 } else {
1029    //                     1
1030    //                 };
1031
1032    //                 let output = format!("\r{}{}{}", left_display, " ".repeat(padding), right_display);
1033    //                 print!("{}", output);
1034    //                 std::io::stdout().flush().unwrap();
1035    //             } else {
1036    //                 print!("\rProcess with PID {} not found in sysinfo", pid);
1037    //                 std::io::stdout().flush().unwrap();
1038    //             }
1039
1040    //             if let Some(status) = handle.child.try_wait()? {
1041    //                 handle.result.exit_status = Some(status);
1042    //                 handle.result.end_time = Some(SystemTime::now());
1043    //                 println!("\nProcess with PID {} finished", pid);
1044    //                 return Ok(handle.result.clone());
1045    //             }
1046
1047    //             std::thread::sleep(POLL_DURATION);
1048    //         }
1049    //     } else {
1050    //         Err(anyhow::anyhow!("Process handle with PID {} not found", pid))
1051    //     }
1052    // }
1053
1054    // pub fn wait(&self, pid: u32, max_polls: Option<usize>) -> anyhow::Result<CargoProcessResult> {
1055    //     let mut processes = self.processes.lock().unwrap();
1056    //     if let Some(handle) = processes.get_mut(&pid) {
1057    //         let mut handle = handle.lock().unwrap();
1058    //         let mut system = System::new_all();
1059
1060    //         // Initialize start_time if not already set.
1061    //         let start_time = handle.result.start_time.unwrap_or_else(|| {
1062    //             let now = SystemTime::now();
1063    //             handle.result.start_time = Some(now);
1064    //             now
1065    //         });
1066    //         // Format the start time with more precision.
1067    //         let start_dt: chrono::DateTime<Local> = start_time.into();
1068    //         let start_str = start_dt.format("%H:%M:%S").to_string();
1069
1070    //         let mut polls = 0;
1071    //         loop {
1072    //             system.refresh_all();
1073
1074    //             if let Some(process) = system.process((pid as usize).into()) {
1075    //                 let now = SystemTime::now();
1076    //                 let runtime = now.duration_since(start_time).unwrap();
1077    //                 let runtime_str = Self::format_duration(runtime);
1078
1079    //                 // Get memory usage and convert to a human-readable string.
1080    //                 let mem_kb = process.memory();
1081    //                 let mem_human = if mem_kb >= 1024 {
1082    //                     format!("{:.2} MB", mem_kb as f64 / 1024.0)
1083    //                 } else {
1084    //                     format!("{} KB", mem_kb)
1085    //                 };
1086
1087    //                 // Build the output string.
1088    //                 let output = format!(
1089    //                     "{} | Runtime: {} | Mem: {} | CPU: {:.2}%%",
1090    //                     start_str,
1091    //                     runtime_str,
1092    //                     mem_human,
1093    //                     process.cpu_usage()
1094    //                 );
1095    //                 // Print on one line and pad to clear leftover characters.
1096    //                 print!("\r{:<80}", output);
1097    //                 std::io::stdout().flush().unwrap();
1098    //             } else {
1099    //                 print!("\rProcess with PID {} not found in sysinfo", pid);
1100    //                 std::io::stdout().flush().unwrap();
1101    //             }
1102
1103    //             // Check if the process has finished.
1104    //             if let Some(status) = handle.child.try_wait()? {
1105    //                 handle.result.exit_status = Some(status);
1106    //                 handle.result.end_time = Some(SystemTime::now());
1107    //                 println!("\nProcess with PID {} finished", pid);
1108    //                 return Ok(handle.result.clone());
1109    //             }
1110
1111    //             polls += 1;
1112    //             if let Some(max) = max_polls {
1113    //                 if polls >= max {
1114    //                     println!("\nReached maximum polling iterations ({})", max);
1115    //                     break;
1116    //                 }
1117    //             }
1118    //             std::thread::sleep(Duration::from_secs(1));
1119    //         }
1120    //         Err(anyhow::anyhow!("Process did not finish after maximum polls"))
1121    //     } else {
1122    //         Err(anyhow::anyhow!("Process handle with PID {} not found", pid))
1123    //     }
1124    // }
1125
1126    //     pub fn wait(&self, pid: u32) -> anyhow::Result<CargoProcessResult> {
1127    //         let mut processes = self.processes.lock().unwrap();
1128    //         if let Some(handle) = processes.get_mut(&pid) {
1129    //             let mut handle = handle.lock().unwrap();
1130
1131    //             loop {
1132    //                 println!("Waiting for process with PID: {}", pid);
1133
1134    //                 let status = handle.child.try_wait()?;
1135
1136    //                 if let Some(status) = status {
1137    //                     handle.result.exit_status = Some(status);
1138    //                     handle.result.end_time = Some(SystemTime::now());
1139    //                     println!("Process with PID {} finished", pid);
1140    //                     return Ok(handle.result.clone());
1141    //                 }
1142
1143    //                 std::thread::sleep(std::time::Duration::from_secs(1));
1144    //             }
1145    //         } else {
1146    //             Err(anyhow::anyhow!("Process handle with PID {} not found", pid))
1147    //         }
1148    // }
1149
1150    pub fn format_process_status(
1151        pid: u32,
1152        start_time: Option<SystemTime>,
1153        system: Arc<Mutex<System>>,
1154        target: &CargoTarget,
1155        target_dimensions: (usize, usize),
1156    ) -> String {
1157        // let start_dt: chrono::DateTime<Local> =
1158        //     start_time.unwrap_or_else(|| SystemTime::UNIX_EPOCH).into();
1159        let start_str = start_time
1160            .map(|st| {
1161                chrono::DateTime::<Local>::from(st)
1162                    .format("%H:%M:%S")
1163                    .to_string()
1164            })
1165            .unwrap_or_else(|| "-".to_string());
1166        let colored_start = nu_ansi_term::Color::LightCyan.paint(&start_str).to_string();
1167        let plain_start = start_str;
1168        if start_time.is_none() {
1169            return String::new();
1170        }
1171        // Refresh the system stats and look up the process.
1172        if let Some(process) = system.lock().unwrap().process((pid as usize).into()) {
1173            let cpu_usage = process.cpu_usage();
1174            let mem_kb = process.memory();
1175            let mem_human = if mem_kb >= 1024 {
1176                format!("{:.2} MB", mem_kb as f64 / 1024.0)
1177            } else {
1178                format!("{} KB", mem_kb)
1179            };
1180
1181            // Calculate runtime.
1182            let now = SystemTime::now();
1183            let runtime_duration = match start_time {
1184                Some(start) => now
1185                    .duration_since(start)
1186                    .unwrap_or_else(|_| Duration::from_secs(0)),
1187                None => Duration::from_secs(0),
1188            };
1189            let runtime_str = crate::e_fmt::format_duration(runtime_duration);
1190            // compute the max number of digits in either dimension:
1191            let max_digits = target_dimensions
1192                .0
1193                .max(target_dimensions.1)
1194                .to_string()
1195                .len();
1196            let left_display = format!(
1197                "{:0width$}of{:0width$} | {} | {} | PID: {} | CPU: {:.2}% | Mem: {}",
1198                target_dimensions.0,
1199                target_dimensions.1,
1200                nu_ansi_term::Color::Green
1201                    .paint(target.display_name.clone())
1202                    .to_string(),
1203                colored_start,
1204                pid,
1205                cpu_usage,
1206                mem_human,
1207                width = max_digits,
1208            );
1209            let left_plain = format!(
1210                "{:0width$}of{:0width$} | {} | {} | PID: {} | CPU: {:.2}% | Mem: {}",
1211                target_dimensions.0,
1212                target_dimensions.1,
1213                target.display_name,
1214                plain_start,
1215                pid,
1216                cpu_usage,
1217                mem_human,
1218                width = max_digits,
1219            );
1220
1221            // Get terminal width.
1222            #[cfg(feature = "tui")]
1223            let (cols, _) = crossterm::terminal::size().unwrap_or((80, 20));
1224            #[cfg(not(feature = "tui"))]
1225            let (cols, _) = (80, 20);
1226            let total_width = cols as usize;
1227
1228            // Format the runtime with underlining.
1229            let right_display = nu_ansi_term::Style::new()
1230                .reset_before_style()
1231                .underline()
1232                .paint(&runtime_str)
1233                .to_string();
1234            let left_len = left_plain.len();
1235            let right_len = runtime_str.len();
1236            let padding = if total_width > left_len + right_len {
1237                total_width - left_len - right_len
1238            } else {
1239                1
1240            };
1241
1242            format!("{}{}{}", left_display, " ".repeat(padding), right_display)
1243        } else {
1244            // String::from("xxx")
1245            format!("\rProcess with PID {} not found in sysinfo", pid)
1246        }
1247    }
1248
1249    /// Print the exact diagnostic output as captured.
1250    pub fn print_exact_output(&self) {
1251        let processes = self.processes.lock().unwrap();
1252        for handle in processes.iter() {
1253            println!("--- Full Diagnostics for PID {} ---", handle.0);
1254            let handle_lock = handle.1.lock().unwrap();
1255            let diags = handle_lock.diagnostics.lock().unwrap();
1256            for diag in diags.iter() {
1257                // Print the entire diagnostic.
1258                println!("{:?}: {}", diag.level, diag.message);
1259            }
1260        }
1261    }
1262
1263    /// Print a one‑line summary per warning, numbered with leading zeros.
1264    pub fn print_prefixed_summary(&self) {
1265        // 1. Grab a snapshot of the handles (Arc clones) under the manager lock.
1266        let guard = self.processes.lock().unwrap();
1267        let handles: Vec<_> = guard.iter().map(|h| h.clone()).collect();
1268
1269        // 2. Now we can iterate without holding the manager lock.
1270        for handle in handles {
1271            // Lock only the diagnostics for this handle.
1272            let handle_lock = handle.1.lock().unwrap();
1273            let diags = handle_lock.diagnostics.lock().unwrap();
1274
1275            // Collect warnings.
1276            let warnings: Vec<_> = diags.iter().filter(|d| d.level.eq("warning")).collect();
1277
1278            // Determine width for zero-padding for warnings.
1279            let warning_width = warnings.len().to_string().len().max(1);
1280            println!(
1281                "\n\n--- Warnings for PID {} --- {} {}",
1282                handle.0,
1283                warning_width,
1284                warnings.len()
1285            );
1286
1287            for (i, diag) in warnings.iter().enumerate() {
1288                // Format the index with leading zeros for warnings.
1289                let index = format!("{:0width$}", i + 1, width = warning_width);
1290                // Print the warning with the index.
1291                println!("{}: {}", index, diag.message.trim());
1292            }
1293
1294            // Collect errors.
1295            let errors: Vec<_> = diags.iter().filter(|d| d.level.eq("error")).collect();
1296
1297            // Determine width for zero-padding for errors.
1298            let error_width = errors.len().to_string().len().max(1);
1299            println!(
1300                "\n\n--- Errors for PID {} --- {} {}",
1301                handle.0,
1302                error_width,
1303                errors.len()
1304            );
1305
1306            for (i, diag) in errors.iter().enumerate() {
1307                // Format the index with leading zeros for errors.
1308                let index = format!("{:0width$}", i + 1, width = error_width);
1309                // Print the error with the index.
1310                println!("{}: {}", index, diag.message.trim());
1311            }
1312        }
1313    }
1314
1315    /// file:line:col – source_line, colored by level.
1316    pub fn print_compact(&self) {
1317        let processes = self.processes.lock().unwrap();
1318        for handle in processes.iter() {
1319            println!("--- Compact for PID {} ---", handle.0);
1320            let handle_lock = handle.1.lock().unwrap();
1321            let diags = handle_lock.diagnostics.lock().unwrap();
1322            for diag in diags.iter() {
1323                println!("{}: {} {}", diag.level, diag.lineref, diag.message.trim());
1324            }
1325        }
1326    }
1327    /// Print a shortened version: warnings first then errors.
1328    pub fn print_shortened_output(&self) {
1329        let processes = self.processes.lock().unwrap();
1330        for handle in processes.iter() {
1331            println!("\n\n\n--- Summary for PID {} ---", handle.0);
1332            let handle_lock = handle.1.lock().unwrap();
1333            let diags = handle_lock.diagnostics.lock().unwrap();
1334
1335            // Filter diagnostics for warnings and errors.
1336            let warnings: Vec<_> = diags.iter().filter(|d| d.level.eq("warning")).collect();
1337            let errors: Vec<_> = diags.iter().filter(|d| d.level.eq("error")).collect();
1338
1339            // Print warnings.
1340            if !warnings.is_empty() {
1341                println!("Warnings:");
1342                for diag in warnings {
1343                    println!("print_shortened_output:{}", diag.message.trim());
1344                }
1345            } else {
1346                println!("No warnings.");
1347            }
1348
1349            // Print errors.
1350            if !errors.is_empty() {
1351                println!("Errors:");
1352                for diag in errors {
1353                    println!("print_shortened_output: {}", diag.message.trim());
1354                }
1355            } else {
1356                println!("No errors.");
1357            }
1358        }
1359    }
1360}
1361
1362// #[cfg(feature = "uses_async")]
1363// impl ProcessManager {
1364//     pub async fn wait_for_processes(&self) {
1365//         loop {
1366//             {
1367//                 if self.processes.lock().unwrap().is_empty() {
1368//                     break;
1369//                 }
1370//             }
1371//             self.notifier.notified().await;
1372//         }
1373//     }
1374// }
1375
1376pub struct CursorGuard;
1377
1378impl Drop for CursorGuard {
1379    fn drop(&mut self) {
1380        #[cfg(feature = "tui")]
1381        {
1382            let mut stdout = std::io::stdout();
1383            let _ = crossterm::execute!(stdout, crossterm::cursor::Show);
1384        }
1385    }
1386}