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                    is_could_not_compile: false, // Placeholder, should be set properly in actual use
830                };
831                self.record_result(result.clone());
832                anyhow::anyhow!("Process handle with PID {} not found", pid)
833            })?
834        };
835
836        // 2. Unwrap Arc<Mutex<...>> to get the handle
837        let mut handle = Arc::try_unwrap(handle_arc)
838            .map_err(|_| anyhow::anyhow!("Process handle for PID {} still shared", pid))?
839            .into_inner()
840            .unwrap();
841
842        // 3. Interactive polling loop
843        let mut system = if handle.is_filter {
844            Some(System::new_all())
845        } else {
846            None
847        };
848        const POLL: Duration = Duration::from_secs(1);
849        let mut loop_cnter = 0;
850        loop {
851            loop_cnter += 1;
852            if handle.is_filter && loop_cnter % 2 == 0 {
853                if let Some(ref mut sys) = system {
854                    sys.refresh_all();
855                }
856            }
857
858            if handle.is_filter {
859                if let Some(ref sys) = system {
860                    if let Some(process) = sys.process((pid as usize).into()) {
861                        let status = handle.format_status(Some(process));
862                        if !status.is_empty() {
863                            print!("\r{}", status);
864                        }
865                    }
866                }
867                std::io::stdout().flush().unwrap();
868            }
869
870            if let Some(es) = handle.child.try_wait()? {
871                let final_diagnostics = {
872                    let diag_lock = handle.diagnostics.lock().unwrap();
873                    diag_lock.clone()
874                };
875                handle.result.diagnostics = final_diagnostics.clone();
876                handle.result.exit_status = Some(es);
877                handle.result.end_time = Some(SystemTime::now());
878                if let (Some(start), Some(end)) = (handle.result.start_time, handle.result.end_time)
879                {
880                    handle.result.elapsed_time =
881                        Some(end.duration_since(start).unwrap_or_default());
882                }
883                println!(
884                    "\nProcess with PID {} finished {:?} {}",
885                    pid,
886                    es,
887                    handle.result.diagnostics.len()
888                );
889                break;
890            }
891            std::thread::sleep(POLL);
892        }
893
894        if handle.is_filter {
895            // 4. Extract diagnostics out of Arc<Mutex<_>>
896            let diagnostics = Arc::try_unwrap(handle.diagnostics)
897                .map(|m| m.into_inner().unwrap())
898                .unwrap_or_else(|arc| arc.lock().unwrap().clone());
899
900            // 5. Move them into the final result
901            handle.result.diagnostics = diagnostics;
902        }
903        self.record_result(handle.result.clone());
904        Ok(handle.result)
905    }
906
907    pub fn record_result(&self, result: CargoProcessResult) {
908        let mut results = self.results.lock().unwrap();
909        results.push(result);
910    }
911
912    pub fn generate_report(&self, create_gist: bool) {
913        let results = self.results.lock().unwrap();
914        let report = crate::e_reports::generate_markdown_report(&results);
915        if let Err(e) = crate::e_reports::save_report_to_file(&report, "run_report.md") {
916            eprintln!("Failed to save report: {}", e);
917        }
918        if create_gist {
919            crate::e_reports::create_gist(&report, "run_report.md").unwrap_or_else(|e| {
920                eprintln!("Failed to create Gist: {}", e);
921            });
922        }
923    }
924    //     pub fn wait(&self, pid: u32, _duration: Option<Duration>) -> anyhow::Result<CargoProcessResult> {
925    //     // Hide the cursor and ensure it is restored on exit.
926    //     {
927    //         let mut stdout = std::io::stdout();
928    //         crossterm::execute!(stdout, crossterm::cursor::Hide)?;
929    //     }
930
931    //     let mut processes = self.processes.lock().unwrap();
932    //     if let Some(handle) = processes.get_mut(&pid) {
933    //         let mut handle = handle.lock().unwrap();
934    //         let mut system = System::new_all();
935
936    //         // Initialize start_time if not already set.
937    //         let start_time = handle.result.start_time.unwrap_or_else(|| {
938    //             let now = SystemTime::now();
939    //             handle.result.start_time = Some(now);
940    //             now
941    //         });
942
943    //         // Main loop.
944    //         const POLL_DURATION: Duration = Duration::from_secs(1);
945    //         loop {
946    //             system.refresh_all();
947    //             let maybe_system: Option<&System> = if true { Some(&system) } else { None };
948    //             // Get formatted status string.
949    //             let output = handle.format_status(maybe_system);
950    //             print!("\r{}", output);
951    //             std::io::stdout().flush().unwrap();
952
953    //             if let Some(status) = handle.child.try_wait()? {
954    //                 handle.result.exit_status = Some(status);
955    //                 handle.result.end_time = Some(SystemTime::now());
956    //                 println!("\nProcess with PID {} finished", pid);
957    //                 return Ok(handle.result.clone());
958    //             }
959    //             std::thread::sleep(POLL_DURATION);
960    //         }
961    //     } else {
962    //         Err(anyhow::anyhow!("Process handle with PID {} not found", pid))
963    //     }
964    // }
965
966    // pub fn wait(&self, pid: u32, _duration: Option<Duration>) -> anyhow::Result<CargoProcessResult> {
967    //     // Turn off (hide) the cursor.
968    //     {
969    //         let mut stdout = std::io::stdout();
970    //         crossterm::execute!(stdout, crossterm::cursor::Hide)?;
971    //     }
972    //     // Ensure the cursor is shown when we exit.
973    //     let _cursor_guard = CursorGuard;
974
975    //     let mut processes = self.processes.lock().unwrap();
976    //     if let Some(handle) = processes.get_mut(&pid) {
977    //         let mut handle = handle.lock().unwrap();
978    //         let mut system = System::new_all();
979
980    //         // Define the poll duration constant (adjust as needed).
981    //         const POLL_DURATION: Duration = Duration::from_secs(1);
982
983    //         // Initialize start_time if not already set.
984    //         let start_time = handle.result.start_time.unwrap_or_else(|| {
985    //             let now = SystemTime::now();
986    //             handle.result.start_time = Some(now);
987    //             now
988    //         });
989    //         // Format the start time with seconds precision.
990    //         let start_dt: chrono::DateTime<Local> = start_time.into();
991    //         let start_str = start_dt.format("%H:%M:%S").to_string();
992    //         // Use ANSI color for the start time.
993    //         let colored_start = nu_ansi_term::Color::Green.paint(&start_str).to_string();
994    //         // Plain version for spacing calculations.
995    //         let plain_start = start_str;
996
997    //         loop {
998    //             system.refresh_all();
999    //             let now = SystemTime::now();
1000    //             let runtime_duration = now.duration_since(start_time).unwrap();
1001    //             let runtime_str = crate::e_fmt::format_duration(runtime_duration);
1002
1003    //             // Use usize conversion with into()
1004    //             if let Some(process) = system.process((pid as usize).into()) {
1005    //                 let cpu_usage = process.cpu_usage();
1006    //                 let mem_kb = process.memory();
1007    //                 let mem_human = if mem_kb >= 1024 {
1008    //                     format!("{:.2} MB", mem_kb as f64 / 1024.0)
1009    //                 } else {
1010    //                     format!("{} KB", mem_kb)
1011    //                 };
1012
1013    //                 let left_display = format!("{} | CPU: {:.2}% | Mem: {}", colored_start, cpu_usage, mem_human);
1014    //                 let left_plain = format!("{} | CPU: {:.2}% | Mem: {}", plain_start, cpu_usage, mem_human);
1015
1016    //                 // Get terminal width with crossterm.
1017    //                 let (cols, _) = crossterm::terminal::size().unwrap_or((80, 20));
1018    //                 let total_width = cols as usize;
1019    //                 // Right side: the elapsed duration, underlined.
1020    //                 let right_display = nu_ansi_term::Style::new()
1021    //                     .reset_before_style()
1022    //                     .underline()
1023    //                     .paint(&runtime_str)
1024    //                     .to_string();
1025    //                 let left_len = left_plain.len();
1026    //                 let right_len = runtime_str.len();
1027    //                 let padding = if total_width > left_len + right_len {
1028    //                     total_width - left_len - right_len
1029    //                 } else {
1030    //                     1
1031    //                 };
1032
1033    //                 let output = format!("\r{}{}{}", left_display, " ".repeat(padding), right_display);
1034    //                 print!("{}", output);
1035    //                 std::io::stdout().flush().unwrap();
1036    //             } else {
1037    //                 print!("\rProcess with PID {} not found in sysinfo", pid);
1038    //                 std::io::stdout().flush().unwrap();
1039    //             }
1040
1041    //             if let Some(status) = handle.child.try_wait()? {
1042    //                 handle.result.exit_status = Some(status);
1043    //                 handle.result.end_time = Some(SystemTime::now());
1044    //                 println!("\nProcess with PID {} finished", pid);
1045    //                 return Ok(handle.result.clone());
1046    //             }
1047
1048    //             std::thread::sleep(POLL_DURATION);
1049    //         }
1050    //     } else {
1051    //         Err(anyhow::anyhow!("Process handle with PID {} not found", pid))
1052    //     }
1053    // }
1054
1055    // pub fn wait(&self, pid: u32, max_polls: Option<usize>) -> anyhow::Result<CargoProcessResult> {
1056    //     let mut processes = self.processes.lock().unwrap();
1057    //     if let Some(handle) = processes.get_mut(&pid) {
1058    //         let mut handle = handle.lock().unwrap();
1059    //         let mut system = System::new_all();
1060
1061    //         // Initialize start_time if not already set.
1062    //         let start_time = handle.result.start_time.unwrap_or_else(|| {
1063    //             let now = SystemTime::now();
1064    //             handle.result.start_time = Some(now);
1065    //             now
1066    //         });
1067    //         // Format the start time with more precision.
1068    //         let start_dt: chrono::DateTime<Local> = start_time.into();
1069    //         let start_str = start_dt.format("%H:%M:%S").to_string();
1070
1071    //         let mut polls = 0;
1072    //         loop {
1073    //             system.refresh_all();
1074
1075    //             if let Some(process) = system.process((pid as usize).into()) {
1076    //                 let now = SystemTime::now();
1077    //                 let runtime = now.duration_since(start_time).unwrap();
1078    //                 let runtime_str = Self::format_duration(runtime);
1079
1080    //                 // Get memory usage and convert to a human-readable string.
1081    //                 let mem_kb = process.memory();
1082    //                 let mem_human = if mem_kb >= 1024 {
1083    //                     format!("{:.2} MB", mem_kb as f64 / 1024.0)
1084    //                 } else {
1085    //                     format!("{} KB", mem_kb)
1086    //                 };
1087
1088    //                 // Build the output string.
1089    //                 let output = format!(
1090    //                     "{} | Runtime: {} | Mem: {} | CPU: {:.2}%%",
1091    //                     start_str,
1092    //                     runtime_str,
1093    //                     mem_human,
1094    //                     process.cpu_usage()
1095    //                 );
1096    //                 // Print on one line and pad to clear leftover characters.
1097    //                 print!("\r{:<80}", output);
1098    //                 std::io::stdout().flush().unwrap();
1099    //             } else {
1100    //                 print!("\rProcess with PID {} not found in sysinfo", pid);
1101    //                 std::io::stdout().flush().unwrap();
1102    //             }
1103
1104    //             // Check if the process has finished.
1105    //             if let Some(status) = handle.child.try_wait()? {
1106    //                 handle.result.exit_status = Some(status);
1107    //                 handle.result.end_time = Some(SystemTime::now());
1108    //                 println!("\nProcess with PID {} finished", pid);
1109    //                 return Ok(handle.result.clone());
1110    //             }
1111
1112    //             polls += 1;
1113    //             if let Some(max) = max_polls {
1114    //                 if polls >= max {
1115    //                     println!("\nReached maximum polling iterations ({})", max);
1116    //                     break;
1117    //                 }
1118    //             }
1119    //             std::thread::sleep(Duration::from_secs(1));
1120    //         }
1121    //         Err(anyhow::anyhow!("Process did not finish after maximum polls"))
1122    //     } else {
1123    //         Err(anyhow::anyhow!("Process handle with PID {} not found", pid))
1124    //     }
1125    // }
1126
1127    //     pub fn wait(&self, pid: u32) -> anyhow::Result<CargoProcessResult> {
1128    //         let mut processes = self.processes.lock().unwrap();
1129    //         if let Some(handle) = processes.get_mut(&pid) {
1130    //             let mut handle = handle.lock().unwrap();
1131
1132    //             loop {
1133    //                 println!("Waiting for process with PID: {}", pid);
1134
1135    //                 let status = handle.child.try_wait()?;
1136
1137    //                 if let Some(status) = status {
1138    //                     handle.result.exit_status = Some(status);
1139    //                     handle.result.end_time = Some(SystemTime::now());
1140    //                     println!("Process with PID {} finished", pid);
1141    //                     return Ok(handle.result.clone());
1142    //                 }
1143
1144    //                 std::thread::sleep(std::time::Duration::from_secs(1));
1145    //             }
1146    //         } else {
1147    //             Err(anyhow::anyhow!("Process handle with PID {} not found", pid))
1148    //         }
1149    // }
1150
1151    pub fn format_process_status(
1152        pid: u32,
1153        start_time: Option<SystemTime>,
1154        system: Arc<Mutex<System>>,
1155        target: &CargoTarget,
1156        target_dimensions: (usize, usize),
1157    ) -> String {
1158        // let start_dt: chrono::DateTime<Local> =
1159        //     start_time.unwrap_or_else(|| SystemTime::UNIX_EPOCH).into();
1160        let start_str = start_time
1161            .map(|st| {
1162                chrono::DateTime::<Local>::from(st)
1163                    .format("%H:%M:%S")
1164                    .to_string()
1165            })
1166            .unwrap_or_else(|| "-".to_string());
1167        let colored_start = nu_ansi_term::Color::LightCyan.paint(&start_str).to_string();
1168        let plain_start = start_str;
1169        if start_time.is_none() {
1170            return String::new();
1171        }
1172        // Refresh the system stats and look up the process.
1173        if let Some(process) = system.lock().unwrap().process((pid as usize).into()) {
1174            let cpu_usage = process.cpu_usage();
1175            let mem_kb = process.memory();
1176            let mem_human = if mem_kb >= 1024 {
1177                format!("{:.2} MB", mem_kb as f64 / 1024.0)
1178            } else {
1179                format!("{} KB", mem_kb)
1180            };
1181
1182            // Calculate runtime.
1183            let now = SystemTime::now();
1184            let runtime_duration = match start_time {
1185                Some(start) => now
1186                    .duration_since(start)
1187                    .unwrap_or_else(|_| Duration::from_secs(0)),
1188                None => Duration::from_secs(0),
1189            };
1190            let runtime_str = crate::e_fmt::format_duration(runtime_duration);
1191            // compute the max number of digits in either dimension:
1192            let max_digits = target_dimensions
1193                .0
1194                .max(target_dimensions.1)
1195                .to_string()
1196                .len();
1197            let left_display = format!(
1198                "{:0width$}of{:0width$} | {} | {} | PID: {} | CPU: {:.2}% | Mem: {}",
1199                target_dimensions.0,
1200                target_dimensions.1,
1201                nu_ansi_term::Color::Green
1202                    .paint(target.display_name.clone())
1203                    .to_string(),
1204                colored_start,
1205                pid,
1206                cpu_usage,
1207                mem_human,
1208                width = max_digits,
1209            );
1210            let left_plain = format!(
1211                "{:0width$}of{:0width$} | {} | {} | PID: {} | CPU: {:.2}% | Mem: {}",
1212                target_dimensions.0,
1213                target_dimensions.1,
1214                target.display_name,
1215                plain_start,
1216                pid,
1217                cpu_usage,
1218                mem_human,
1219                width = max_digits,
1220            );
1221
1222            // Get terminal width.
1223            #[cfg(feature = "tui")]
1224            let (cols, _) = crossterm::terminal::size().unwrap_or((80, 20));
1225            #[cfg(not(feature = "tui"))]
1226            let (cols, _) = (80, 20);
1227            let total_width = cols as usize;
1228
1229            // Format the runtime with underlining.
1230            let right_display = nu_ansi_term::Style::new()
1231                .reset_before_style()
1232                .underline()
1233                .paint(&runtime_str)
1234                .to_string();
1235            let left_len = left_plain.len();
1236            let right_len = runtime_str.len();
1237            let padding = if total_width > left_len + right_len {
1238                total_width - left_len - right_len
1239            } else {
1240                1
1241            };
1242
1243            format!("{}{}{}", left_display, " ".repeat(padding), right_display)
1244        } else {
1245            // String::from("xxx")
1246            format!("\rProcess with PID {} not found in sysinfo", pid)
1247        }
1248    }
1249
1250    /// Print the exact diagnostic output as captured.
1251    pub fn print_exact_output(&self) {
1252        let processes = self.processes.lock().unwrap();
1253        for handle in processes.iter() {
1254            println!("--- Full Diagnostics for PID {} ---", handle.0);
1255            let handle_lock = handle.1.lock().unwrap();
1256            let diags = handle_lock.diagnostics.lock().unwrap();
1257            for diag in diags.iter() {
1258                // Print the entire diagnostic.
1259                println!("{:?}: {}", diag.level, diag.message);
1260            }
1261        }
1262    }
1263
1264    /// Print a one‑line summary per warning, numbered with leading zeros.
1265    pub fn print_prefixed_summary(&self) {
1266        // 1. Grab a snapshot of the handles (Arc clones) under the manager lock.
1267        let guard = self.processes.lock().unwrap();
1268        let handles: Vec<_> = guard.iter().map(|h| h.clone()).collect();
1269
1270        // 2. Now we can iterate without holding the manager lock.
1271        for handle in handles {
1272            // Lock only the diagnostics for this handle.
1273            let handle_lock = handle.1.lock().unwrap();
1274            let diags = handle_lock.diagnostics.lock().unwrap();
1275
1276            // Collect warnings.
1277            let warnings: Vec<_> = diags.iter().filter(|d| d.level.eq("warning")).collect();
1278
1279            // Determine width for zero-padding for warnings.
1280            let warning_width = warnings.len().to_string().len().max(1);
1281            println!(
1282                "\n\n--- Warnings for PID {} --- {} {}",
1283                handle.0,
1284                warning_width,
1285                warnings.len()
1286            );
1287
1288            for (i, diag) in warnings.iter().enumerate() {
1289                // Format the index with leading zeros for warnings.
1290                let index = format!("{:0width$}", i + 1, width = warning_width);
1291                // Print the warning with the index.
1292                println!("{}: {}", index, diag.message.trim());
1293            }
1294
1295            // Collect errors.
1296            let errors: Vec<_> = diags.iter().filter(|d| d.level.eq("error")).collect();
1297
1298            // Determine width for zero-padding for errors.
1299            let error_width = errors.len().to_string().len().max(1);
1300            println!(
1301                "\n\n--- Errors for PID {} --- {} {}",
1302                handle.0,
1303                error_width,
1304                errors.len()
1305            );
1306
1307            for (i, diag) in errors.iter().enumerate() {
1308                // Format the index with leading zeros for errors.
1309                let index = format!("{:0width$}", i + 1, width = error_width);
1310                // Print the error with the index.
1311                println!("{}: {}", index, diag.message.trim());
1312            }
1313        }
1314    }
1315
1316    /// file:line:col – source_line, colored by level.
1317    pub fn print_compact(&self) {
1318        let processes = self.processes.lock().unwrap();
1319        for handle in processes.iter() {
1320            println!("--- Compact for PID {} ---", handle.0);
1321            let handle_lock = handle.1.lock().unwrap();
1322            let diags = handle_lock.diagnostics.lock().unwrap();
1323            for diag in diags.iter() {
1324                println!("{}: {} {}", diag.level, diag.lineref, diag.message.trim());
1325            }
1326        }
1327    }
1328    /// Print a shortened version: warnings first then errors.
1329    pub fn print_shortened_output(&self) {
1330        let processes = self.processes.lock().unwrap();
1331        for handle in processes.iter() {
1332            println!("\n\n\n--- Summary for PID {} ---", handle.0);
1333            let handle_lock = handle.1.lock().unwrap();
1334            let diags = handle_lock.diagnostics.lock().unwrap();
1335
1336            // Filter diagnostics for warnings and errors.
1337            let warnings: Vec<_> = diags.iter().filter(|d| d.level.eq("warning")).collect();
1338            let errors: Vec<_> = diags.iter().filter(|d| d.level.eq("error")).collect();
1339
1340            // Print warnings.
1341            if !warnings.is_empty() {
1342                println!("Warnings:");
1343                for diag in warnings {
1344                    println!("print_shortened_output:{}", diag.message.trim());
1345                }
1346            } else {
1347                println!("No warnings.");
1348            }
1349
1350            // Print errors.
1351            if !errors.is_empty() {
1352                println!("Errors:");
1353                for diag in errors {
1354                    println!("print_shortened_output: {}", diag.message.trim());
1355                }
1356            } else {
1357                println!("No errors.");
1358            }
1359        }
1360    }
1361}
1362
1363// #[cfg(feature = "uses_async")]
1364// impl ProcessManager {
1365//     pub async fn wait_for_processes(&self) {
1366//         loop {
1367//             {
1368//                 if self.processes.lock().unwrap().is_empty() {
1369//                     break;
1370//                 }
1371//             }
1372//             self.notifier.notified().await;
1373//         }
1374//     }
1375// }
1376
1377pub struct CursorGuard;
1378
1379impl Drop for CursorGuard {
1380    fn drop(&mut self) {
1381        #[cfg(feature = "tui")]
1382        {
1383            let mut stdout = std::io::stdout();
1384            let _ = crossterm::execute!(stdout, crossterm::cursor::Show);
1385        }
1386    }
1387}