Skip to content

Commit 61888be

Browse files
Copilotszmyty
andauthored
refactor: extract create_output_bar helper and define render symbol constants
Agent-Logs-Url: https://github.com/egohygiene/renderflow/sessions/20715bbe-0279-4e3f-bd9f-d21808f1dd38 Co-authored-by: szmyty <14865041+szmyty@users.noreply.github.com>
1 parent c500374 commit 61888be

1 file changed

Lines changed: 32 additions & 20 deletions

File tree

src/commands/build.rs

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,27 @@ pub fn run_resilient(config_path: &str) -> Result<()> {
4545
/// cache and dependency map after all formats have finished.
4646
type RenderResult = (String, String, Result<()>, Option<String>, Option<Vec<FileDependency>>);
4747

48+
/// Progress-bar symbols used in render status messages.
49+
const SYMBOL_SKIP: &str = "↩";
50+
const SYMBOL_OK: &str = "✔";
51+
const SYMBOL_FAIL: &str = "✘";
52+
53+
/// Create and register a per-output spinner progress bar on `mp`.
54+
///
55+
/// Pre-creating bars before the rayon parallel section avoids calling
56+
/// [`MultiProgress::add`] from multiple threads, which would acquire an
57+
/// internal mutex on every call and could serialise parallel workers.
58+
fn create_output_bar(mp: &MultiProgress, format_label: &str) -> ProgressBar {
59+
let bar = mp.add(ProgressBar::new_spinner());
60+
bar.set_style(
61+
ProgressStyle::with_template(" {spinner:.blue} [{prefix:.bold.cyan}] {msg}")
62+
.expect("hardcoded per-output progress bar template is valid"),
63+
);
64+
bar.set_prefix(format_label.to_string());
65+
bar.set_message("queued");
66+
bar
67+
}
68+
4869
fn run_impl(config_path: &str, dry_run: bool, resilient: bool, optimization: Option<OptimizationMode>) -> Result<()> {
4970
if dry_run {
5071
info!("Dry-run mode enabled — no files will be created and no commands will be executed");
@@ -127,18 +148,9 @@ fn run_impl(config_path: &str, dry_run: bool, resilient: bool, optimization: Opt
127148
);
128149

129150
// Pre-create one spinner per output format *before* the parallel rendering
130-
// section. Calling mp.add() inside a rayon parallel closure acquires an
131-
// internal mutex on every call and can serialise the workers. By creating
132-
// all bars up-front each worker can update its own bar without contention.
151+
// section. See [`create_output_bar`] for the rationale.
133152
let output_bars: Vec<ProgressBar> = config.outputs.iter().map(|output| {
134-
let bar = mp.add(ProgressBar::new_spinner());
135-
bar.set_style(
136-
ProgressStyle::with_template(" {spinner:.blue} [{prefix:.bold.cyan}] {msg}")
137-
.expect("hardcoded per-output progress bar template is valid"),
138-
);
139-
bar.set_prefix(output.output_type.to_string());
140-
bar.set_message("queued");
141-
bar
153+
create_output_bar(&mp, &output.output_type.to_string())
142154
}).collect();
143155

144156
// Transforms are run once per output format (serially, before parallel rendering)
@@ -249,7 +261,7 @@ fn run_impl(config_path: &str, dry_run: bool, resilient: bool, optimization: Opt
249261

250262
if dry_run {
251263
info!("[DRY RUN] Would render {} output to: {}", format, output_path);
252-
bar.finish_with_message(format!("[DRY RUN] Would render to {}", output_path));
264+
bar.finish_with_message(format!("[DRY RUN] {SYMBOL_OK} Would render to {}", output_path));
253265
pb.inc(1);
254266
pb.println(format!("[DRY RUN] Would write output to: {}", output_path));
255267
(format_str, output_path, Ok(()), None, None)
@@ -307,9 +319,9 @@ fn run_impl(config_path: &str, dry_run: bool, resilient: bool, optimization: Opt
307319
{
308320
debug!(hash = %output_hash, output = %output_path, "Output cache hit — skipping render");
309321
info!("Skipping {} render (unchanged)", format);
310-
bar.finish_with_message(format!(" unchanged: {}", output_path));
322+
bar.finish_with_message(format!("{SYMBOL_SKIP} unchanged: {}", output_path));
311323
pb.inc(1);
312-
pb.println(format!(" Skipping {} output (unchanged): {}", format, output_path));
324+
pb.println(format!("{SYMBOL_SKIP} Skipping {} output (unchanged): {}", format, output_path));
313325
return (format_str, output_path, Ok(()), Some(output_hash), Some(file_deps));
314326
}
315327

@@ -329,16 +341,16 @@ fn run_impl(config_path: &str, dry_run: bool, resilient: bool, optimization: Opt
329341

330342
match &result {
331343
Ok(_) => {
332-
bar.finish_with_message(format!(" {}", output_path));
344+
bar.finish_with_message(format!("{SYMBOL_OK} {}", output_path));
333345
pb.inc(1);
334-
pb.println(format!(" Output written to: {}", output_path));
346+
pb.println(format!("{SYMBOL_OK} Output written to: {}", output_path));
335347
info!(output = %output_path, "Pipeline completed for format: {}", format);
336348
}
337349
Err(e) => {
338350
warn!(format = %format, error = %e, "Rendering failed for output format");
339-
bar.finish_with_message(format!(" failed: {:#}", e));
351+
bar.finish_with_message(format!("{SYMBOL_FAIL} failed: {:#}", e));
340352
pb.inc(1);
341-
pb.println(format!(" Failed to render {} output: {:#}", format, e));
353+
pb.println(format!("{SYMBOL_FAIL} Failed to render {} output: {:#}", format, e));
342354
}
343355
}
344356
(format_str, output_path, result, new_hash, new_deps)
@@ -373,9 +385,9 @@ fn run_impl(config_path: &str, dry_run: bool, resilient: bool, optimization: Opt
373385
.collect();
374386

375387
if dry_run {
376-
pb.finish_with_message("[DRY RUN] Dry-run complete — no output written");
388+
pb.finish_with_message(format!("[DRY RUN] {SYMBOL_OK} Dry-run complete — no output written"));
377389
} else if failed_outputs.is_empty() {
378-
pb.finish_with_message("✔ Build complete");
390+
pb.finish_with_message(format!("{SYMBOL_OK} Build complete"));
379391
} else {
380392
pb.finish_with_message(format!("⚠ Build completed with {} failure(s)", failed_outputs.len()));
381393
let messages: Vec<String> = failed_outputs

0 commit comments

Comments
 (0)