cargo_e/
e_cargocommand_ext.rs

1use crate::e_command_builder::{CargoCommandBuilder, TerminalError};
2use crate::e_eventdispatcher::{EventDispatcher, ThreadLocalContext};
3#[allow(unused_imports)]
4use cargo_metadata::Message;
5use nu_ansi_term::{Color, Style};
6#[cfg(feature = "uses_serde")]
7use serde_json;
8use std::collections::VecDeque;
9use std::io::{BufRead, BufReader, Read};
10use std::process::ExitStatus;
11use std::process::{Child, Command, Stdio};
12#[allow(unused_imports)]
13use std::sync::atomic::{AtomicUsize, Ordering};
14use std::sync::{Arc, Mutex};
15use std::time::{Duration, SystemTime};
16use std::{fmt, thread};
17// enum CaptureMode {
18//     Filtering(DispatcherSet),
19//     Passthrough { stdout: std::io::Stdout, stderr: std::io::Stderr },
20// }
21// struct DispatcherSet {
22//     stdout: Option<Arc<EventDispatcher>>,
23//     stderr: Option<Arc<EventDispatcher>>,
24//     progress: Option<Arc<EventDispatcher>>,
25//     stage: Option<Arc<EventDispatcher>>,
26// }
27
28/// CargoStats tracks counts for different cargo events and also stores the first occurrence times.
29#[derive(Debug, Default)]
30pub struct CargoStats {
31    pub target_name: String,
32    pub is_comiler_target: bool,
33    pub is_could_not_compile: bool,
34    pub start_time: Option<SystemTime>,
35    pub compiler_message_count: usize,
36    pub compiler_artifact_count: usize,
37    pub build_script_executed_count: usize,
38    pub build_finished_count: usize,
39    // Record the first occurrence of each stage.
40    pub compiler_message_time: Option<SystemTime>,
41    pub compiler_artifact_time: Option<SystemTime>,
42    pub build_script_executed_time: Option<SystemTime>,
43    pub build_finished_time: Option<SystemTime>,
44}
45
46#[derive(Clone)]
47pub struct CargoDiagnostic {
48    pub lineref: String,
49    pub level: String,
50    pub message: String,
51    pub error_code: Option<String>,
52    pub suggestion: Option<String>,
53    pub note: Option<String>,
54    pub help: Option<String>,
55    pub uses_color: bool,
56    pub diag_number: Option<usize>,
57    pub diag_num_padding: Option<usize>,
58}
59
60impl CargoDiagnostic {
61    pub fn print_short(&self) {
62        // Render the full Debug output
63        let full = format!("{:?}", self);
64        // Grab only the first line (or an empty string if somehow there isn't one)
65        let first = full.lines().next().unwrap_or("");
66        println!("{}", first);
67    }
68}
69impl CargoDiagnostic {
70    #[allow(clippy::too_many_arguments)]
71    pub fn new(
72        lineref: String,
73        level: String,
74        message: String,
75        error_code: Option<String>,
76        suggestion: Option<String>,
77        note: Option<String>,
78        help: Option<String>,
79        uses_color: bool,
80        diag_number: Option<usize>,
81        diag_num_padding: Option<usize>,
82    ) -> Self {
83        CargoDiagnostic {
84            lineref,
85            level,
86            message,
87            error_code,
88            suggestion,
89            note,
90            help,
91            uses_color,
92            diag_number,
93            diag_num_padding,
94        }
95    }
96
97    fn update_suggestion_with_lineno(
98        &self,
99        suggestion: &str,
100        file: String,
101        line_number: usize,
102    ) -> String {
103        // Regex to match line number in the suggestion (e.g., "79 | fn clean<S: AsRef<str>>(s: S) -> String {")
104        let suggestion_regex = regex::Regex::new(r"(?P<line>\d+)\s*\|\s*(.*)").unwrap();
105
106        // Iterate through suggestion lines and check line numbers
107        suggestion
108            .lines()
109            .filter_map(|line| {
110                let binding = line.replace(['|', '^', '-', '_'], "");
111                let cleaned_line = binding.trim();
112
113                // If the line becomes empty after removing | and ^, skip it
114                if cleaned_line.is_empty() {
115                    return None; // Skip empty lines
116                }
117
118                if let Some(caps) = suggestion_regex.captures(line.trim()) {
119                    let suggestion_line: usize = caps["line"].parse().unwrap_or(line_number); // If parsing fails, use the default line number
120                                                                                              // Replace the line number if it doesn't match the diagnostic's line number
121                    if suggestion_line != line_number {
122                        return Some(format!(
123                            "{}:{} | {}",
124                            file,
125                            suggestion_line, // Replace with the actual diagnostic line number
126                            caps.get(2).map_or("", |m| m.as_str())
127                        ));
128                    } else {
129                        // If the line number matches, return the original suggestion line without line number
130                        return Some(
131                            caps.get(2)
132                                .map_or("".to_owned(), |m| m.as_str().to_string()),
133                        );
134                    }
135                }
136                Some(line.to_string())
137            })
138            .collect::<Vec<String>>()
139            .join("\n")
140    }
141}
142
143impl fmt::Debug for CargoDiagnostic {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        // Capitalize the first letter of the level
146        let level_cap = self
147            .level
148            .chars()
149            .next()
150            .unwrap_or(' ')
151            .to_uppercase()
152            .to_string();
153
154        // Extract the file and line number from lineref (e.g., "cargo-e\src\e_command_builder.rs:79:8")
155        let lineref_regex = regex::Regex::new(r"(?P<file>.*):(?P<line>\d+):(?P<col>\d+)").unwrap();
156        let lineref_caps = lineref_regex.captures(&self.lineref);
157
158        let file = lineref_caps
159            .as_ref()
160            .and_then(|caps| caps.name("file").map(|m| m.as_str().to_string()))
161            .unwrap_or_else(|| "unknown file".to_string());
162        let line_number: usize = lineref_caps
163            .as_ref()
164            .and_then(|caps| caps.name("line").map(|m| m.as_str().parse().unwrap_or(0)))
165            .unwrap_or(0);
166
167        // Format the diagnostic number with padding
168        let padded_diag_number = if let Some(dn) = &self.diag_number {
169            format!("{:0width$}", dn, width = self.diag_num_padding.unwrap_or(0))
170        } else {
171            String::new()
172        };
173
174        // Combine the level and diagnostic number
175        let diag_number = format!("{}{}:", level_cap, padded_diag_number);
176
177        // Color the diagnostic number if color is enabled
178        let diag_number_colored = if self.uses_color {
179            match self.level.as_str() {
180                "warning" => Color::Yellow.paint(&diag_number).to_string(),
181                "error" => Color::Red.paint(&diag_number).to_string(),
182                "help" => Color::Purple.paint(&diag_number).to_string(),
183                _ => Color::Green.paint(&diag_number).to_string(),
184            }
185        } else {
186            diag_number.clone()
187        };
188
189        // Print the diagnostic number and lineref
190        if self.uses_color {
191            write!(f, "{} ", diag_number_colored)?;
192            let underlined_text = Style::new().underline().paint(&self.lineref).to_string();
193            write!(f, "\x1b[4m{}\x1b[0m ", underlined_text)?; // Apply underline using ANSI codes
194        } else {
195            write!(f, "{} ", diag_number)?;
196            write!(f, "{} ", self.lineref)?; // Plain lineref without color
197        }
198
199        // Print the message
200        write!(f, "{}: ", self.message)?;
201
202        // Print the suggestion if present
203        if let Some(suggestion) = &self.suggestion {
204            let suggestion = self.update_suggestion_with_lineno(suggestion, file, line_number);
205            let suggestion = suggestion.replace(
206                "help: if this is intentional, prefix it with an underscore: ",
207                "",
208            );
209            let suggestion = regex::Regex::new(r"\^{3,}")
210                .map(|re| re.replace_all(&suggestion, "^^").to_string())
211                .unwrap_or_else(|_| suggestion.clone());
212
213            let suggestion = regex::Regex::new(r"-{3,}")
214                .map(|re| re.replace_all(&suggestion, "^-").to_string())
215                .unwrap_or_else(|_| suggestion.clone());
216
217            let suggestion_text = if self.uses_color {
218                Color::Green.paint(suggestion.clone()).to_string()
219            } else {
220                suggestion.clone()
221            };
222
223            let mut lines = suggestion.lines();
224            if let Some(first_line) = lines.next() {
225                let mut formatted_suggestion = first_line.to_string();
226                for line in lines {
227                    if line.trim_start().starts_with(['|', '^', '-', '_']) {
228                        let cleaned_line = line
229                            .trim_start_matches(|c| c == '|' || c == '^' || c == '-' || c == '_')
230                            .trim_start();
231
232                        let cleaned_line = if self.uses_color {
233                            Color::Blue
234                                .paint(&format!(" // {}", cleaned_line))
235                                .to_string()
236                        } else {
237                            format!(" // {}", cleaned_line)
238                        };
239
240                        formatted_suggestion.push_str(&cleaned_line);
241                    } else {
242                        formatted_suggestion.push('\n');
243                        formatted_suggestion.push_str(line);
244                    }
245                }
246
247                let formatted_suggestion = formatted_suggestion
248                    .lines()
249                    .map(|line| format!("     {}", line))
250                    .collect::<Vec<String>>()
251                    .join("\n");
252                write!(f, "{} ", formatted_suggestion)?;
253            } else {
254                let suggestion_text = suggestion_text
255                    .lines()
256                    .map(|line| format!("     {}", line))
257                    .collect::<Vec<String>>()
258                    .join("\n");
259                write!(f, "{} ", suggestion_text)?;
260            }
261        }
262
263        // Print the note if present
264        if let Some(note) = &self.note {
265            let note = regex::Regex::new(r"for more information, see <(https?://[^>]+)>")
266                .map(|re| re.replace_all(&note, "$1").to_string())
267                .unwrap_or_else(|_| note.clone());
268
269            let note = regex::Regex::new(r"`#\[warn\((.*?)\)\]` on by default")
270                .map(|re| re.replace_all(&note, "#[allow($1)]").to_string())
271                .unwrap_or_else(|_| note.clone());
272            let note = if self.uses_color {
273                Color::Blue.paint(&note).to_string()
274            } else {
275                note.clone()
276            };
277            let formatted_note = note
278                .lines()
279                .map(|line| format!("  {}", line))
280                .collect::<Vec<String>>()
281                .join("\n");
282            write!(f, "\n{}", formatted_note)?;
283        }
284
285        // Print the help if present
286        if let Some(help) = &self.help {
287            let help_text = if self.uses_color {
288                Color::LightYellow.paint(help).to_string()
289            } else {
290                help.clone()
291            };
292            write!(f, "\n  {} ", help_text)?;
293        }
294
295        // Finish the debug formatting
296        write!(f, "") // No further fields are needed
297    }
298}
299
300/// CargoProcessResult is returned when the cargo process completes.
301#[derive(Debug, Default, Clone)]
302pub struct CargoProcessResult {
303    pub target_name: String,
304    pub cmd: String,
305    pub args: Vec<String>,
306    pub pid: u32,
307    pub terminal_error: Option<TerminalError>,
308    pub exit_status: Option<ExitStatus>,
309    pub start_time: Option<SystemTime>,
310    pub build_finished_time: Option<SystemTime>,
311    pub end_time: Option<SystemTime>,
312    pub build_elapsed: Option<Duration>,
313    pub runtime_elapsed: Option<Duration>,
314    pub elapsed_time: Option<Duration>,
315    pub stats: CargoStats,
316    pub build_output_size: usize,
317    pub runtime_output_size: usize,
318    pub diagnostics: Vec<CargoDiagnostic>,
319    pub is_filter: bool,
320    pub is_could_not_compile: bool,
321}
322
323impl CargoProcessResult {
324    /// Print every diagnostic in full detail.
325    pub fn print_exact(&self) {
326        if self.diagnostics.is_empty() {
327            return;
328        }
329        println!(
330            "--- Full Diagnostics for PID {} --- {}",
331            self.pid,
332            self.diagnostics.len()
333        );
334        for diag in &self.diagnostics {
335            println!("{:?}", diag);
336        }
337    }
338
339    /// Print warnings first, then errors, one‐line summary.
340    pub fn print_short(&self) {
341        if self.diagnostics.is_empty() {
342            return;
343        }
344        // let warnings: Vec<_> = self.diagnostics.iter()
345        //     .filter(|d| d.level.eq("warning"))
346        //     .collect();
347        let errors: Vec<_> = self
348            .diagnostics
349            .iter()
350            .filter(|d| d.level.eq("error"))
351            .collect();
352
353        // println!("--- Warnings ({} total) ---", warnings.len());
354        // for d in warnings {
355        //     d.print_short();
356        // }
357
358        println!("--- Errors ({} total) ---", errors.len());
359        for d in errors {
360            d.print_short();
361        }
362    }
363
364    /// Print a compact, zero‑padded, numbered list of *all* diagnostics.
365    pub fn print_compact(&self) {
366        if self.diagnostics.is_empty() {
367            return;
368        }
369        let total = self.diagnostics.len();
370        let pid = self.pid; // Assuming `pid` is accessible in this context
371        println!("--- All Diagnostics for PID {} ({} total) ---", pid, total);
372
373        for diag in self.diagnostics.iter() {
374            let max_lineref_len = self
375                .diagnostics
376                .iter()
377                .map(|d| d.lineref.len())
378                .max()
379                .unwrap_or(0);
380
381            let padded_diag_number = if let Some(dn) = &diag.diag_number {
382                format!("{:0width$}", dn, width = diag.diag_num_padding.unwrap_or(0))
383            } else {
384                String::new()
385            };
386
387            if diag.level != "help" && diag.level != "note" {
388                println!(
389                    "{}{}: {:<width$} {}",
390                    diag.level,
391                    padded_diag_number,
392                    diag.lineref,
393                    diag.message.lines().next().unwrap_or("").trim(),
394                    width = max_lineref_len
395                );
396            }
397        }
398    }
399}
400
401/// CargoProcessHandle holds the cargo process and related state.
402#[derive(Debug)]
403pub struct CargoProcessHandle {
404    pub child: Child,
405    pub result: CargoProcessResult,
406    pub pid: u32,
407    pub requested_exit: bool,
408    pub stdout_handle: thread::JoinHandle<()>,
409    pub stderr_handle: thread::JoinHandle<()>,
410    pub start_time: SystemTime,
411    pub stats: Arc<Mutex<CargoStats>>,
412    pub stdout_dispatcher: Option<Arc<EventDispatcher>>,
413    pub stderr_dispatcher: Option<Arc<EventDispatcher>>,
414    pub progress_dispatcher: Option<Arc<EventDispatcher>>,
415    pub stage_dispatcher: Option<Arc<EventDispatcher>>,
416    pub estimate_bytes: Option<usize>,
417    // Separate progress counters for build and runtime output.
418    pub build_progress_counter: Arc<AtomicUsize>,
419    pub runtime_progress_counter: Arc<AtomicUsize>,
420    pub terminal_error_flag: Arc<Mutex<TerminalError>>,
421    pub diagnostics: Arc<Mutex<Vec<CargoDiagnostic>>>,
422    pub is_filter: bool,
423    pub removed: bool,
424}
425
426impl CargoProcessHandle {
427    pub fn print_results(result: &CargoProcessResult) {
428        let start_time = result.start_time.unwrap_or(SystemTime::now());
429        println!("-------------------------------------------------");
430        println!("Process started at: {:?}", result.start_time);
431        if let Some(build_time) = result.build_finished_time {
432            println!("Build phase ended at: {:?}", build_time);
433            println!(
434                "Build phase elapsed:  {}",
435                crate::e_fmt::format_duration(
436                    build_time
437                        .duration_since(start_time)
438                        .unwrap_or_else(|_| Duration::new(0, 0))
439                )
440            );
441        } else {
442            println!("No BuildFinished timestamp recorded.");
443        }
444        println!("Process ended at:   {:?}", result.end_time);
445        if let Some(runtime_dur) = result.runtime_elapsed {
446            println!(
447                "Runtime phase elapsed: {}",
448                crate::e_fmt::format_duration(runtime_dur)
449            );
450        }
451        if let Some(build_dur) = result.build_elapsed {
452            println!(
453                "Build phase elapsed:   {}",
454                crate::e_fmt::format_duration(build_dur)
455            );
456        }
457        if let Some(total_elapsed) = result
458            .end_time
459            .and_then(|end| end.duration_since(start_time).ok())
460        {
461            println!(
462                "Total elapsed time:   {}",
463                crate::e_fmt::format_duration(total_elapsed)
464            );
465        } else {
466            println!("No total elapsed time available.");
467        }
468        println!(
469            "Build output size:  {} ({} bytes)",
470            crate::e_fmt::format_bytes(result.build_output_size),
471            result.build_output_size
472        );
473        println!(
474            "Runtime output size: {} ({} bytes)",
475            crate::e_fmt::format_bytes(result.runtime_output_size),
476            result.runtime_output_size
477        );
478        println!("-------------------------------------------------");
479    }
480
481    /// Kill the cargo process if needed.
482    pub fn kill(&mut self) -> std::io::Result<()> {
483        self.child.kill()
484    }
485    pub fn pid(&self) -> u32 {
486        self.pid
487    }
488
489    //     pub fn wait(&mut self) -> std::io::Result<CargoProcessResult> {
490    //     // Lock the instance since `self` is an `Arc`
491    //     // let mut cargo_process_handle = self.lock().unwrap();  // `lock()` returns a mutable reference
492
493    //     // Call wait on the child process
494    //     let status = self.child.wait()?;  // Call wait on the child process
495
496    //     println!("Cargo process finished with status: {:?}", status);
497
498    //     let end_time = SystemTime::now();
499
500    //     // Retrieve the statistics from the process handle
501    //     let stats = Arc::try_unwrap(self.stats.clone())
502    //         .map(|mutex| mutex.into_inner().unwrap())
503    //         .unwrap_or_else(|arc| (*arc.lock().unwrap()).clone());
504
505    //     let build_out = self.build_progress_counter.load(Ordering::Relaxed);
506    //     let runtime_out = self.runtime_progress_counter.load(Ordering::Relaxed);
507
508    //     // Calculate phase durations if build_finished_time is recorded
509    //     let (build_elapsed, runtime_elapsed) = if let Some(build_finished) = stats.build_finished_time {
510    //         let build_dur = build_finished.duration_since(self.start_time)
511    //             .unwrap_or_else(|_| Duration::new(0, 0));
512    //         let runtime_dur = end_time.duration_since(build_finished)
513    //             .unwrap_or_else(|_| Duration::new(0, 0));
514    //         (Some(build_dur), Some(runtime_dur))
515    //     } else {
516    //         (None, None)
517    //     };
518
519    //     self.result.exit_status = Some(status);
520    //     self.result.end_time = Some(end_time);
521    //     self.result.build_output_size = self.build_progress_counter.load(Ordering::Relaxed);
522    //     self.result.runtime_output_size = self.runtime_progress_counter.load(Ordering::Relaxed);
523
524    //     Ok(self.result.clone())
525    //     // Return the final process result
526    //     // Ok(CargoProcessResult {
527    //     //     pid: self.pid,
528    //     //     exit_status: Some(status),
529    //     //     start_time: Some(self.start_time),
530    //     //     build_finished_time: stats.build_finished_time,
531    //     //     end_time: Some(end_time),
532    //     //     build_elapsed,
533    //     //     runtime_elapsed,
534    //     //     stats,
535    //     //     build_output_size: build_out,
536    //     //     runtime_output_size: runtime_out,
537    //     // })
538    // }
539
540    //  pub fn wait(self: Arc<Self>) -> std::io::Result<CargoProcessResult> {
541    //     let mut global = GLOBAL_CHILDREN.lock().unwrap();
542
543    //     // Lock and access the CargoProcessHandle inside the Mutex
544    //     if let Some(cargo_process_handle) = global.iter_mut().find(|handle| {
545    //         handle.lock().unwrap().pid == self.pid  // Compare the pid to find the correct handle
546    //     }) {
547    //         let mut cargo_process_handle = cargo_process_handle.lock().unwrap();  // Mutably borrow the process handle
548
549    //         let status = cargo_process_handle.child.wait()?;  // Call wait on the child process
550
551    //         println!("Cargo process finished with status: {:?}", status);
552
553    //         let end_time = SystemTime::now();
554
555    //         // Retrieve the statistics from the process handle
556    //         let stats = Arc::try_unwrap(cargo_process_handle.stats.clone())
557    //             .map(|mutex| mutex.into_inner().unwrap())
558    //             .unwrap_or_else(|arc| (*arc.lock().unwrap()).clone());
559
560    //         let build_out = cargo_process_handle.build_progress_counter.load(Ordering::Relaxed);
561    //         let runtime_out = cargo_process_handle.runtime_progress_counter.load(Ordering::Relaxed);
562
563    //         // Calculate phase durations if build_finished_time is recorded
564    //         let (build_elapsed, runtime_elapsed) = if let Some(build_finished) = stats.build_finished_time {
565    //             let build_dur = build_finished.duration_since(cargo_process_handle.start_time)
566    //                 .unwrap_or_else(|_| Duration::new(0, 0));
567    //             let runtime_dur = end_time.duration_since(build_finished)
568    //                 .unwrap_or_else(|_| Duration::new(0, 0));
569    //             (Some(build_dur), Some(runtime_dur))
570    //         } else {
571    //             (None, None)
572    //         };
573
574    //         // Return the final process result
575    //         Ok(CargoProcessResult {
576    //             exit_status: status,
577    //             start_time: cargo_process_handle.start_time,
578    //             build_finished_time: stats.build_finished_time,
579    //             end_time,
580    //             build_elapsed,
581    //             runtime_elapsed,
582    //             stats,
583    //             build_output_size: build_out,
584    //             runtime_output_size: runtime_out,
585    //         })
586    //     } else {
587    //         Err(std::io::Error::new(std::io::ErrorKind::NotFound, "Process handle not found").into())
588    //     }
589    // }
590
591    // Wait for the process and output threads to finish.
592    // Computes elapsed times for the build phase and runtime phase, and returns a CargoProcessResult.
593    // pub fn wait(mut self) -> std::io::Result<CargoProcessResult> {
594    //     let status = self.child.wait()?;
595    //     println!("Cargo process finished with status: {:?}", status);
596
597    //     self.stdout_handle.join().expect("stdout thread panicked");
598    //     self.stderr_handle.join().expect("stderr thread panicked");
599
600    //     let end_time = SystemTime::now();
601
602    //     // Retrieve the statistics.
603    //     let stats = Arc::try_unwrap(self.stats)
604    //         .map(|mutex| mutex.into_inner().unwrap())
605    //         .unwrap_or_else(|arc| (*arc.lock().unwrap()).clone());
606
607    //     let build_out = self.build_progress_counter.load(Ordering::Relaxed);
608    //     let runtime_out = self.runtime_progress_counter.load(Ordering::Relaxed);
609
610    //     // Calculate phase durations if build_finished_time is recorded.
611    //     let (build_elapsed, runtime_elapsed) = if let Some(build_finished) = stats.build_finished_time {
612    //         let build_dur = build_finished.duration_since(self.start_time).unwrap_or_else(|_| Duration::new(0, 0));
613    //         let runtime_dur = end_time.duration_since(build_finished).unwrap_or_else(|_| Duration::new(0, 0));
614    //         (Some(build_dur), Some(runtime_dur))
615    //     } else {
616    //         (None, None)
617    //     };
618
619    //     Ok(CargoProcessResult {
620    //         exit_status: status,
621    //         start_time: self.start_time,
622    //         build_finished_time: stats.build_finished_time,
623    //         end_time,
624    //         build_elapsed,
625    //         runtime_elapsed,
626    //         stats,
627    //         build_output_size: build_out,
628    //         runtime_output_size: runtime_out,
629    //     })
630    // }
631
632    /// Returns a formatted status string.
633    /// If `system` is provided, CPU/memory and runtime info is displayed on the right.
634    /// Otherwise, only the start time is shown.
635    pub fn format_status(&self, process: Option<&sysinfo::Process>) -> String {
636        // Ensure the start time is available.
637        let start_time = self
638            .result
639            .start_time
640            .expect("start_time should be initialized");
641        let start_dt: chrono::DateTime<chrono::Local> = start_time.into();
642        let start_str = start_dt.format("%H:%M:%S").to_string();
643        // Use ANSI coloring for the left display.
644        let colored_start = nu_ansi_term::Color::Green.paint(&start_str).to_string();
645
646        if let Some(process) = process {
647            // if let Some(process) = system.process((self.pid as usize).into()) {
648            let cpu_usage = process.cpu_usage();
649            let mem_kb = process.memory();
650            let mem_human = if mem_kb >= 1024 {
651                format!("{:.2} MB", mem_kb as f64 / 1024.0)
652            } else {
653                format!("{} KB", mem_kb)
654            };
655
656            let now = SystemTime::now();
657            let runtime_duration = now.duration_since(start_time).unwrap();
658            let runtime_str = crate::e_fmt::format_duration(runtime_duration);
659
660            let left_display = format!(
661                "{} | CPU: {:.2}% | Mem: {}",
662                colored_start, cpu_usage, mem_human
663            );
664            // Use plain text for length calculations.
665            let left_plain = format!(
666                "{} | CPU: {:.2}% | Mem: {}",
667                start_str, cpu_usage, mem_human
668            );
669
670            // Get terminal width.
671            #[cfg(feature = "tui")]
672            let (cols, _) = crossterm::terminal::size().unwrap_or((80, 20));
673            #[cfg(not(feature = "tui"))]
674            let (cols, _) = (80, 20);
675            let total_width = cols as usize;
676
677            // Format the runtime info with underlining.
678            let right_display = nu_ansi_term::Style::new()
679                .reset_before_style()
680                .underline()
681                .paint(&runtime_str)
682                .to_string();
683            let left_len = left_plain.len();
684            let right_len = runtime_str.len();
685            let padding = if total_width > left_len + right_len {
686                total_width - left_len - right_len
687            } else {
688                1
689            };
690
691            let ret = format!("{}{}{}", left_display, " ".repeat(padding), right_display);
692            if ret.trim().is_empty() {
693                String::from("No output available")
694            } else {
695                ret
696            }
697        } else {
698            // return format!("Process {} not found",(self.pid as usize));
699            String::new()
700        }
701        // } else {
702        //     // If system monitoring is disabled, just return the start time.
703        //     colored_start
704        // }
705    }
706}
707
708/// Extension trait to add cargo-specific capture capabilities to Command.
709pub trait CargoCommandExt {
710    fn spawn_cargo_capture(
711        &mut self,
712        builder: Arc<CargoCommandBuilder>,
713        stdout_dispatcher: Option<Arc<EventDispatcher>>,
714        stderr_dispatcher: Option<Arc<EventDispatcher>>,
715        progress_dispatcher: Option<Arc<EventDispatcher>>,
716        stage_dispatcher: Option<Arc<EventDispatcher>>,
717        estimate_bytes: Option<usize>,
718    ) -> CargoProcessHandle;
719    fn spawn_cargo_passthrough(&mut self, builder: Arc<CargoCommandBuilder>) -> CargoProcessHandle;
720}
721
722impl CargoCommandExt for Command {
723    fn spawn_cargo_passthrough(&mut self, builder: Arc<CargoCommandBuilder>) -> CargoProcessHandle {
724        // Spawn the child process without redirecting stdout and stderr
725        let child = self.spawn().unwrap_or_else(|_| {
726            panic!(
727                "Failed to spawn cargo process {:?} {:?}",
728                &builder.alternate_cmd, builder.args
729            )
730        });
731
732        let pid = child.id();
733        let start_time = SystemTime::now();
734        let diagnostics = Arc::clone(&builder.diagnostics);
735        let s = CargoStats {
736            is_comiler_target: builder.is_compiler_target(), // Ensure this field is now valid
737            start_time: Some(start_time),
738            build_finished_time: Some(start_time),
739            target_name: builder.target_name.clone(),
740            ..Default::default()
741        };
742        let stats = Arc::new(Mutex::new(s.clone()));
743        // Try to take ownership of the Vec<CargoDiagnostic> out of the Arc.
744
745        let (cmd, args) = builder.injected_args();
746        // Create the CargoProcessHandle
747        let result = CargoProcessResult {
748            target_name: builder.target_name.clone(),
749            cmd,
750            args,
751            pid,
752            terminal_error: None,
753            exit_status: None,
754            start_time: Some(start_time),
755            build_finished_time: None,
756            end_time: None,
757            elapsed_time: None,
758            build_elapsed: None,
759            runtime_elapsed: None,
760            stats: s,
761            build_output_size: 0,
762            runtime_output_size: 0,
763            diagnostics: Vec::new(),
764            is_filter: builder.is_filter,
765            is_could_not_compile: false,
766        };
767
768        // Return the CargoProcessHandle that owns the child process
769        CargoProcessHandle {
770            child,  // The child process is now owned by the handle
771            result, // The result contains information about the process
772            pid,    // The PID of the process
773            stdout_handle: thread::spawn(move || {
774                // This thread is now unnecessary if we are not capturing anything
775                // We can leave it empty or remove it altogether
776            }),
777            stderr_handle: thread::spawn(move || {
778                // This thread is also unnecessary if we are not capturing anything
779            }),
780            start_time,
781            stats,
782            stdout_dispatcher: None,   // No dispatcher is needed
783            stderr_dispatcher: None,   // No dispatcher is needed
784            progress_dispatcher: None, // No dispatcher is needed
785            stage_dispatcher: None,    // No dispatcher is needed
786            estimate_bytes: None,
787            build_progress_counter: Arc::new(AtomicUsize::new(0)),
788            runtime_progress_counter: Arc::new(AtomicUsize::new(0)),
789            requested_exit: false,
790            terminal_error_flag: Arc::new(Mutex::new(TerminalError::NoError)),
791            diagnostics,
792            is_filter: builder.is_filter,
793            removed: false, // Initially not removed
794        }
795    }
796
797    fn spawn_cargo_capture(
798        &mut self,
799        builder: Arc<CargoCommandBuilder>,
800        stdout_dispatcher: Option<Arc<EventDispatcher>>,
801        stderr_dispatcher: Option<Arc<EventDispatcher>>,
802        progress_dispatcher: Option<Arc<EventDispatcher>>,
803        stage_dispatcher: Option<Arc<EventDispatcher>>,
804        estimate_bytes: Option<usize>,
805    ) -> CargoProcessHandle {
806        self.stdout(Stdio::piped()).stderr(Stdio::piped());
807        let builder_for_result = builder.clone();
808        let builder_for_closure = builder.clone();
809        let builder_stdout = builder.clone();
810        let builder_stderr = builder.clone();
811        // println!("Spawning cargo process with capture {:?}",self);
812        let mut child = self.spawn().expect("Failed to spawn cargo process");
813        let pid = child.id();
814        let start_time = SystemTime::now();
815        let diagnostics = Arc::new(Mutex::new(Vec::<CargoDiagnostic>::new()));
816        let s = CargoStats {
817            target_name: builder.target_name.clone(),
818            is_comiler_target: builder.is_compiler_target(),
819            is_could_not_compile: false,
820            start_time: Some(start_time),
821            ..Default::default()
822        };
823        let stats = Arc::new(Mutex::new(s));
824
825        // Two separate counters: one for build output and one for runtime output.
826        let stderr_compiler_msg = Arc::new(Mutex::new(VecDeque::<String>::new()));
827        let build_progress_counter = Arc::new(AtomicUsize::new(0));
828        let runtime_progress_counter = Arc::new(AtomicUsize::new(0));
829
830        // Clone dispatchers and counters for use in threads.
831        let _stdout_disp_clone = stdout_dispatcher.clone();
832        let progress_disp_clone_stdout = progress_dispatcher.clone();
833        let stage_disp_clone = stage_dispatcher.clone();
834
835        let stats_stdout_clone = Arc::clone(&stats);
836        let stats_stderr_clone = Arc::clone(&stats);
837        let _build_counter_stdout = Arc::clone(&build_progress_counter);
838        let _runtime_counter_stdout = Arc::clone(&runtime_progress_counter);
839
840        // Spawn a thread to process stdout.
841        let _stderr_compiler_msg_clone = Arc::clone(&stderr_compiler_msg);
842        let stdout = child.stdout.take().expect("Failed to capture stdout");
843        // println!("{}: Capturing stdout", pid);
844        let stdout_handle = thread::spawn(move || {
845            ThreadLocalContext::set_context(
846                &builder_stdout.target_name.clone(),
847                builder_stdout.manifest_path.to_str().unwrap_or_default(),
848            );
849
850            let stdout_reader = BufReader::new(stdout);
851            // This flag marks whether we are still in the build phase.
852            #[allow(unused_mut)]
853            let mut _in_build_phase = true;
854            let stdout_buffer = Arc::new(Mutex::new(Vec::<String>::new()));
855            let buf = Arc::clone(&stdout_buffer);
856            {
857                for line in stdout_reader.lines().map(|line| line) {
858                    if let Ok(line) = line {
859                        // println!("{}: {}", pid, line);
860                        // Try to parse the line as a JSON cargo message.
861
862                        #[cfg(not(feature = "uses_serde"))]
863                        println!("{}", line);
864                        #[cfg(feature = "uses_serde")]
865                        match serde_json::from_str::<Message>(&line) {
866                            Ok(msg) => {
867                                // let msg_str = format!("{:?}", msg);
868                                // if let Some(ref disp) = stdout_disp_clone {
869                                //     disp.dispatch(&msg_str);
870                                // }
871                                // Add message length to the appropriate counter.
872                                // if in_build_phase {
873                                //     build_counter_stdout.fetch_add(msg_str.len(), Ordering::Relaxed);
874                                // } else {
875                                //     runtime_counter_stdout.fetch_add(msg_str.len(), Ordering::Relaxed);
876                                // }
877                                if let Some(total) = estimate_bytes {
878                                    let current = if _in_build_phase {
879                                        _build_counter_stdout.load(Ordering::Relaxed)
880                                    } else {
881                                        _runtime_counter_stdout.load(Ordering::Relaxed)
882                                    };
883                                    let progress = (current as f64 / total as f64) * 100.0;
884                                    if let Some(ref pd) = progress_disp_clone_stdout {
885                                        pd.dispatch(
886                                            &format!("Progress: {:.2}%", progress),
887                                            stats_stdout_clone.clone(),
888                                        );
889                                    }
890                                }
891
892                                let now = SystemTime::now();
893                                // Process known cargo message variants.
894                                match msg {
895                                    Message::BuildFinished(_) => {
896                                        // Mark the end of the build phase.
897                                        if _in_build_phase {
898                                            _in_build_phase = false;
899                                            let mut s = stats_stdout_clone.lock().unwrap();
900                                            s.build_finished_count += 1;
901                                            s.build_finished_time.get_or_insert(now);
902                                            drop(s);
903                                            // self.result.build_finished_time = Some(now);
904                                            if let Some(ref sd) = stage_disp_clone {
905                                                sd.dispatch(
906                                                    &format!(
907                                                        "Stage: BuildFinished occurred at {:?}",
908                                                        now
909                                                    ),
910                                                    stats_stdout_clone.clone(),
911                                                );
912                                            }
913                                            if let Some(ref sd) = stage_disp_clone {
914                                                sd.dispatch(
915                                                    "Stage: Switching to runtime passthrough",
916                                                    stats_stdout_clone.clone(),
917                                                );
918                                            }
919                                        }
920                                    }
921                                    Message::CompilerMessage(msg) => {
922                                        // println!("parsed{}: {:?}", pid, msg);
923                                        let mut s = stats_stdout_clone.lock().unwrap();
924                                        s.compiler_message_count += 1;
925                                        if s.compiler_message_time.is_none() {
926                                            s.compiler_message_time = Some(now);
927                                            drop(s);
928                                            if let Some(ref sd) = stage_disp_clone {
929                                                sd.dispatch(
930                                                    &format!(
931                                                        "Stage: CompilerMessage occurred at {:?}",
932                                                        now
933                                                    ),
934                                                    stats_stdout_clone.clone(),
935                                                );
936                                            }
937                                        }
938                                        let mut msg_vec =
939                                            _stderr_compiler_msg_clone.lock().unwrap();
940                                        msg_vec.push_back(format!(
941                                            "{}\n\n",
942                                            msg.message.rendered.unwrap_or_default()
943                                        ));
944                                        // let mut diags = diagnostics.lock().unwrap();
945                                        // let diag = crate::e_eventdispatcher::convert_message_to_diagnostic(msg, &msg_str);
946                                        // diags.push(diag.clone());
947                                        // if let Some(ref sd) = stage_disp_clone {
948                                        //     sd.dispatch(&format!("Stage: Diagnostic occurred at {:?}", now));
949                                        // }
950                                    }
951                                    Message::CompilerArtifact(_a) => {
952                                        let mut s = stats_stdout_clone.lock().unwrap();
953                                        // if let Some(exe) = a.executable.as_deref() {
954                                        //     if !exe.as_str().is_empty() {
955                                        //         let m = crate::GLOBAL_MANAGER.get();
956                                        //         if let Some(ref m) = m {
957                                        //             // println!(
958                                        //             //     "Persisting executable for target {}: {}",
959                                        //             //     builder.target_name, exe
960                                        //             // );
961                                        //             // if let Err(e) = m.persist_executable_for_target(
962                                        //             //     &builder.target_name,
963                                        //             //     exe.as_str()
964                                        //             // ) {
965                                        //             //     eprintln!(
966                                        //             //         "Error persisting executable for target {}: {}",
967                                        //             //         builder.target_name, e
968                                        //             //     );
969                                        //             // }
970
971                                        //         }
972                                        //     }
973                                        // }
974                                        s.compiler_artifact_count += 1;
975                                        if s.compiler_artifact_time.is_none() {
976                                            s.compiler_artifact_time = Some(now);
977                                            drop(s);
978                                            if let Some(ref sd) = stage_disp_clone {
979                                                sd.dispatch(
980                                                    &format!(
981                                                        "Stage: CompilerArtifact occurred at {:?}",
982                                                        now
983                                                    ),
984                                                    stats_stdout_clone.clone(),
985                                                );
986                                            }
987                                        }
988                                    }
989                                    Message::BuildScriptExecuted(_) => {
990                                        let mut s = stats_stdout_clone.lock().unwrap();
991                                        s.build_script_executed_count += 1;
992                                        if s.build_script_executed_time.is_none() {
993                                            s.build_script_executed_time = Some(now);
994                                            drop(s);
995                                            if let Some(ref sd) = stage_disp_clone {
996                                                sd.dispatch(
997                                                    &format!(
998                                                    "Stage: BuildScriptExecuted occurred at {:?}",
999                                                    now
1000                                                ),
1001                                                    stats_stdout_clone.clone(),
1002                                                );
1003                                            }
1004                                        }
1005                                    }
1006                                    _ => {}
1007                                }
1008                            }
1009                            Err(_err) => {
1010                                // println!("ERROR {} {}: {}",_err, pid, line);
1011                                // If JSON parsing fails, assume this is plain runtime output.
1012                                // If still in build phase, we assume the build phase has ended.
1013                                if _in_build_phase {
1014                                    _in_build_phase = false;
1015                                    let now = SystemTime::now();
1016                                    let mut s = stats_stdout_clone.lock().unwrap();
1017                                    s.build_finished_count += 1;
1018                                    s.build_finished_time.get_or_insert(now);
1019                                    drop(s);
1020                                    if let Some(ref sd) = stage_disp_clone {
1021                                        sd.dispatch(
1022                                            &format!(
1023                                                "Stage: BuildFinished (assumed) occurred at {:?}",
1024                                                now
1025                                            ),
1026                                            stats_stdout_clone.clone(),
1027                                        );
1028                                    }
1029                                    buf.lock().unwrap().push(line.to_string());
1030                                } else {
1031                                    // build is done: first flush anything we buffered
1032                                    let mut b = buf.lock().unwrap();
1033                                    for l in b.drain(..) {
1034                                        println!("{}", l);
1035                                    }
1036                                    // then print live
1037                                    println!("{}", line);
1038                                }
1039                                if let Some(ref disp) = _stdout_disp_clone {
1040                                    disp.dispatch(&line, stats_stdout_clone.clone());
1041                                }
1042                                // Print the runtime output.
1043                                // println!("{}: {}", pid, line);
1044                                if line.contains("not a terminal") {
1045                                    println!(
1046                                        "{}NOT A TERMINAL - MARK AND RUN AGAIN: {}",
1047                                        pid, line
1048                                    );
1049                                }
1050                                _runtime_counter_stdout.fetch_add(line.len(), Ordering::Relaxed);
1051                                if let Some(total) = estimate_bytes {
1052                                    let current = _runtime_counter_stdout.load(Ordering::Relaxed);
1053                                    let progress = (current as f64 / total as f64) * 100.0;
1054                                    if let Some(ref pd) = progress_disp_clone_stdout {
1055                                        pd.dispatch(
1056                                            &format!("Progress: {:.2}%", progress),
1057                                            stats_stdout_clone.clone(),
1058                                        );
1059                                    }
1060                                }
1061                            }
1062                        }
1063                    }
1064                }
1065            }
1066        }); // End of stdout thread
1067        let tflag = TerminalError::NoError;
1068        // Create a flag to indicate if the process is a terminal process.
1069        let terminal_flag = Arc::new(Mutex::new(TerminalError::NoError));
1070        // Spawn a thread to capture stderr.
1071        let stderr = child.stderr.take().expect("Failed to capture stderr");
1072        let stderr_disp_clone = stderr_dispatcher.clone();
1073        // let terminal_flag_clone = Arc::clone(&terminal_flag);
1074        // let build_counter_stderr = Arc::clone(&build_progress_counter);
1075        // let runtime_counter_stderr = Arc::clone(&runtime_progress_counter);
1076        // let progress_disp_clone_stderr = progress_dispatcher.clone();
1077        let escape_sequence = "\u{1b}[1m\u{1b}[32m";
1078        // let diagnostics_clone = Arc::clone(&diagnostics);
1079        let stderr_compiler_msg_clone = Arc::clone(&stderr_compiler_msg);
1080        let mut stderr_reader = BufReader::new(stderr);
1081        let stderr_handle = thread::spawn(move || {
1082            ThreadLocalContext::set_context(
1083                &builder_stderr.target_name.clone(),
1084                builder_stderr.manifest_path.to_str().unwrap_or_default(),
1085            );
1086            //    let mut msg_vec = stderr_compiler_msg_clone.lock().unwrap();
1087            loop {
1088                // println!("looping stderr thread {}", pid);
1089                // Lock the deque and pop all messages available in a while loop
1090                while let Some(message) = {
1091                    let mut guard = match stderr_compiler_msg_clone.lock() {
1092                        Ok(guard) => guard,
1093                        Err(err) => {
1094                            eprintln!("Failed to lock stderr_compiler_msg_clone: {}", err);
1095                            return; // Exit the function or loop in case of an error
1096                        }
1097                    };
1098                    guard.pop_front()
1099                } {
1100                    for line in message.lines().map(|line| line) {
1101                        if let Some(ref disp) = stderr_disp_clone {
1102                            // Dispatch the line and receive the Vec<Option<CallbackResponse>>.
1103                            let responses = disp.dispatch(line, stats_stderr_clone.clone());
1104
1105                            // Iterate over the responses.
1106                            for ret in responses {
1107                                if let Some(response) = ret {
1108                                    if response.terminal_status == Some(TerminalError::NoTerminal) {
1109                                        // If the response indicates a terminal error, set the flag.
1110                                        println!("{} IS A TERMINAL PROCESS - {}", pid, line);
1111                                    } else if response.terminal_status
1112                                        == Some(TerminalError::NoError)
1113                                    {
1114                                        // If the response indicates no terminal error, set the flag to NoError.
1115                                    } else if response.terminal_status
1116                                        == Some(TerminalError::NoTerminal)
1117                                    {
1118                                        // If the response indicates not a terminal, set the flag to NoTerminal.
1119                                        println!("{} IS A TERMINAL PROCESS - {}", pid, line);
1120                                    }
1121                                    // if let Some(ref msg) = response.message {
1122                                    //     println!("DISPATCH RESULT {} {}", pid, msg);
1123                                    // // }
1124                                    //             let diag = crate::e_eventdispatcher::convert_response_to_diagnostic(response, &line);
1125                                    //             // let mut diags = diagnostics_clone.lock().unwrap();
1126
1127                                    //             let in_multiline = disp.callbacks
1128                                    //             .lock().unwrap()
1129                                    //             .iter()
1130                                    //             .any(|cb| cb.is_reading_multiline.load(Ordering::Relaxed));
1131
1132                                    //         if !in_multiline {
1133                                    //             // start of a new diagnostic
1134                                    //             diags.push(diag);
1135                                    //         } else {
1136                                    //             // continuation → child of the last diagnostic
1137                                    //             if let Some(parent) = diags.last_mut() {
1138                                    //                 parent.children.push(diag);
1139                                    //             } else {
1140                                    //                 // no parent yet (unlikely), just push
1141                                    //                 diags.push(diag);
1142                                    //             }
1143                                    //         }
1144                                }
1145                            }
1146                        }
1147                    }
1148                }
1149                // Sleep briefly if no messages are available to avoid busy waiting
1150                thread::sleep(Duration::from_millis(100));
1151                // If still in build phase, add to the build counter.
1152                // break;
1153
1154                // println!("{}: dave stderr", pid);
1155                // let mut flag = terminal_flag_clone.lock().unwrap();
1156                for line in stderr_reader.by_ref().lines() {
1157                    // println!("SPAWN{}: {:?}", pid, line);
1158                    if let Ok(line) = line {
1159                        // if line.contains("IO(Custom { kind: NotConnected") {
1160                        //     println!("{} IS A TERMINAL PROCESS - {}", pid,line);
1161                        //     continue;
1162                        // }
1163                        let line = if line.starts_with(escape_sequence) {
1164                            // If the line starts with the escape sequence, preserve it and remove leading spaces
1165                            let rest_of_line = &line[escape_sequence.len()..]; // Get the part of the line after the escape sequence
1166                            format!("{}{}", escape_sequence, rest_of_line.trim_start())
1167                        // Reassemble the escape sequence and the trimmed text
1168                        } else {
1169                            line // If it doesn't start with the escape sequence, leave it unchanged
1170                        };
1171                        if let Some(ref disp) = stderr_disp_clone {
1172                            // Dispatch the line and receive the Vec<Option<CallbackResponse>>.
1173                            let responses = disp.dispatch(&line, stats_stderr_clone.clone());
1174                            let mut has_match = false;
1175                            // Iterate over the responses.
1176                            for ret in responses {
1177                                if let Some(_response) = ret {
1178                                    has_match = true;
1179                                    // if response.terminal_status == Some(TerminalError::NoTerminal) {
1180                                    //     // If the response indicates a terminal error, set the flag.
1181                                    //     *flag = TerminalError::NoTerminal;
1182                                    //     println!("{} IS A TERMINAL PROCESS - {}", pid, line);
1183                                    // } else if response.terminal_status == Some(TerminalError::NoError) {
1184                                    //     // If the response indicates no terminal error, set the flag to NoError.
1185                                    //     *flag = TerminalError::NoError;
1186                                    // } else if response.terminal_status == Some(TerminalError::NoTerminal) {
1187                                    //     // If the response indicates not a terminal, set the flag to NoTerminal.
1188                                    //      *flag = TerminalError::NoTerminal;
1189                                    //     println!("{} IS A TERMINAL PROCESS - {}", pid, line);
1190                                    // }
1191                                    // if let Some(ref msg) = response.message {
1192                                    //      println!("DISPATCH RESULT {} {}", pid, msg);
1193                                    // }
1194                                    //     let diag = crate::e_eventdispatcher::convert_response_to_diagnostic(response, &line);
1195                                    //     // let mut diags = diagnostics_clone.lock().unwrap();
1196
1197                                    //     let in_multiline = disp.callbacks
1198                                    //     .lock().unwrap()
1199                                    //     .iter()
1200                                    //     .any(|cb| cb.is_reading_multiline.load(Ordering::Relaxed));
1201
1202                                    // if !in_multiline {
1203                                    //     // start of a new diagnostic
1204                                    //     diags.push(diag);
1205                                    // } else {
1206                                    //     // continuation → child of the last diagnostic
1207                                    //     if let Some(parent) = diags.last_mut() {
1208                                    //         parent.children.push(diag);
1209                                    //     } else {
1210                                    //         // no parent yet (unlikely), just push
1211                                    //         diags.push(diag);
1212                                    //     }
1213                                    // }
1214                                }
1215                            }
1216                            if !has_match && !line.trim().is_empty() && !line.eq("...") {
1217                                // If the line doesn't match any pattern, print it as is.
1218                                println!("{}", line);
1219                            }
1220                        } else {
1221                            println!("ALLLINES {}", line.trim()); //all lines
1222                        }
1223                        // if let Some(ref disp) = stderr_disp_clone {
1224                        //     if let Some(ret) = disp.dispatch(&line) {
1225                        //         if let Some(ref msg) = ret.message {
1226                        //             println!("DISPATCH RESULT {} {}", pid, msg);
1227                        //         }
1228                        //     }
1229                        // }
1230                        // // Here, we assume stderr is less structured. We add its length to runtime counter.
1231                        // runtime_counter_stderr.fetch_add(line.len(), Ordering::Relaxed);
1232                        // if let Some(total) = estimate_bytes {
1233                        //     let current = runtime_counter_stderr.load(Ordering::Relaxed);
1234                        //     let progress = (current as f64 / total as f64) * 100.0;
1235                        //     if let Some(ref pd) = progress_disp_clone_stderr {
1236                        //         pd.dispatch(&format!("Progress: {:.2}%", progress));
1237                        //     }
1238                        // }
1239                    }
1240                }
1241                // println!("{}: dave stderr end", pid);
1242            } //loop
1243        }); // End of stderr thread
1244
1245        let final_diagnostics = {
1246            let diag_lock = diagnostics.lock().unwrap();
1247            diag_lock.clone()
1248        };
1249        let (cmd, args) = builder_for_result.injected_args();
1250        let stats_snapshot = stats.lock().unwrap().clone();
1251        // Wait for the child process to finish and get its exit status.
1252        let exit_status = match child.try_wait() {
1253            Ok(Some(status)) => Some(status),
1254            Ok(None) => None,
1255            Err(e) => {
1256                eprintln!("Failed to check child process exit status {:?}", e);
1257                None
1258            }
1259        };
1260        let result = CargoProcessResult {
1261            target_name: builder_for_closure.target_name.clone(),
1262            cmd,
1263            args,
1264            pid,
1265            exit_status,
1266            start_time: Some(start_time),
1267            build_finished_time: None,
1268            end_time: None,
1269            elapsed_time: None,
1270            build_elapsed: None,
1271            runtime_elapsed: None,
1272            stats: stats_snapshot.clone(),
1273            build_output_size: 0,
1274            runtime_output_size: 0,
1275            terminal_error: Some(tflag),
1276            diagnostics: final_diagnostics,
1277            is_filter: builder_for_closure.is_filter,
1278            is_could_not_compile: stats_snapshot.is_could_not_compile,
1279        };
1280        CargoProcessHandle {
1281            child,
1282            result,
1283            pid,
1284            stdout_handle,
1285            stderr_handle,
1286            start_time,
1287            stats,
1288            stdout_dispatcher,
1289            stderr_dispatcher,
1290            progress_dispatcher,
1291            stage_dispatcher,
1292            estimate_bytes,
1293            build_progress_counter,
1294            runtime_progress_counter,
1295            requested_exit: false,
1296            terminal_error_flag: terminal_flag,
1297            diagnostics,
1298            is_filter: builder_for_result.is_filter,
1299            removed: false, // Initially not removed
1300        }
1301    }
1302}
1303
1304impl Clone for CargoStats {
1305    fn clone(&self) -> Self {
1306        CargoStats {
1307            target_name: self.target_name.clone(),
1308            is_comiler_target: self.is_comiler_target,
1309            is_could_not_compile: self.is_could_not_compile,
1310            start_time: self.start_time,
1311            compiler_message_count: self.compiler_message_count,
1312            compiler_artifact_count: self.compiler_artifact_count,
1313            build_script_executed_count: self.build_script_executed_count,
1314            build_finished_count: self.build_finished_count,
1315            compiler_message_time: self.compiler_message_time,
1316            compiler_artifact_time: self.compiler_artifact_time,
1317            build_script_executed_time: self.build_script_executed_time,
1318            build_finished_time: self.build_finished_time,
1319        }
1320    }
1321}