From a570327a6be45a0cdeac420ccb21f526f806b7df Mon Sep 17 00:00:00 2001 From: teernisse Date: Sat, 14 Feb 2026 11:36:48 -0500 Subject: [PATCH] refactor(progress): extract format_stage_line with themed styling Pull the line-formatting logic out of finish_stage() into a standalone public format_stage_line() so that sync.rs can build stage lines without needing a live ProgressBar (e.g. for static multi-line blocks printed after the spinner is cleared). The new function applies Theme::info().bold() to the label and Theme::timing() to the elapsed column, giving every stage line consistent color treatment. finish_stage() now delegates to it. Includes a unit test asserting the formatted output contains the expected icon, label, summary, and elapsed components. --- src/cli/progress.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/cli/progress.rs b/src/cli/progress.rs index ac41092..ce10cc7 100644 --- a/src/cli/progress.rs +++ b/src/cli/progress.rs @@ -4,7 +4,7 @@ use std::sync::LazyLock; use std::time::Duration; use tracing_subscriber::fmt::MakeWriter; -use crate::cli::render::Icons; +use crate::cli::render::{Icons, Theme}; static MULTI: LazyLock = LazyLock::new(MultiProgress::new); @@ -56,12 +56,21 @@ pub fn nested_progress(msg: &str, len: u64, robot_mode: bool) -> ProgressBar { /// /// Output: ` ✓ Label summary elapsed` pub fn finish_stage(pb: &ProgressBar, icon: &str, label: &str, summary: &str, elapsed: Duration) { - let elapsed_str = format_elapsed(elapsed); - let line = format!(" {icon} {label:<12}{summary:>40} {elapsed_str:>8}",); + let line = format_stage_line(icon, label, summary, elapsed); pb.set_style(ProgressStyle::with_template("{msg}").expect("valid template")); pb.finish_with_message(line); } +/// Build a static stage line showing icon, label, summary, and elapsed. +/// +/// Output: ` ✓ Label summary elapsed` +pub fn format_stage_line(icon: &str, label: &str, summary: &str, elapsed: Duration) -> String { + let elapsed_str = format_elapsed(elapsed); + let styled_label = Theme::info().bold().render(&format!("{label:<12}")); + let styled_elapsed = Theme::timing().render(&format!("{elapsed_str:>8}")); + format!(" {icon} {styled_label}{summary:>40} {styled_elapsed}") +} + /// Format a Duration as a compact human string (e.g. "1.2s", "42ms", "1m 5s"). fn format_elapsed(d: Duration) -> String { let ms = d.as_millis(); @@ -203,4 +212,13 @@ mod tests { assert_eq!(format_elapsed(Duration::from_secs(65)), "1m 5s"); assert_eq!(format_elapsed(Duration::from_secs(120)), "2m 0s"); } + + #[test] + fn format_stage_line_includes_label_summary_and_elapsed() { + let line = format_stage_line("✔", "Issues", "10 issues", Duration::from_millis(4200)); + assert!(line.contains("✔")); + assert!(line.contains("Issues")); + assert!(line.contains("10 issues")); + assert!(line.contains("4.2s")); + } }