From 4908c6190b3b744e7bee6b705263808d27ed6a80 Mon Sep 17 00:00:00 2001 From: VeryBaaad Date: Wed, 22 Apr 2026 01:04:55 +0800 Subject: [PATCH 1/2] feat: integrate indicatif for terminal progress bar Replace the manual `\r`-based progress counter with `indicatif` to provide a cleaner, more reliable progress UI. The previous implementation was visually inconsistent and prone to terminal corruption under multi-threaded execution. --- Cargo.toml | 1 + src/main.rs | 30 +++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9864dca..31a698b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" anyhow = "1.0.102" clap = { version = "4.6.0", features = ["derive"] } filetime = "0.2.27" +indicatif = "0.18.4" rayon = "1.11.0" walkdir = "2.5.0" diff --git a/src/main.rs b/src/main.rs index 960c79b..da25b8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use anyhow::{Context, Result}; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicUsize, Ordering}; use rayon::prelude::*; +use indicatif::{ProgressBar, ProgressStyle}; mod random; mod file_ops; @@ -87,36 +88,47 @@ fn main() -> Result<()> { println!("Processing {} files with {} threads...", files.len(), threads); - let processed = AtomicUsize::new(0); let failed = AtomicUsize::new(0); let errors = std::sync::Mutex::new(Vec::<(PathBuf, String)>::new()); + let pb = if args.verbose { + None + } else { + let bar = ProgressBar::new(files.len() as u64); + bar.set_style( + ProgressStyle::default_bar() + .template("[{elapsed_precise}] [{bar:40}] {pos}/{len} ({eta}) {msg}") + .unwrap() + .progress_chars("##-"), + ); + Some(bar) + }; files.par_iter().for_each(|path| { let res = process_file(path, args.verbose, random_source, args.dry_run); - let count = processed.fetch_add(1, Ordering::Relaxed) + 1; if let Err(e) = res { failed.fetch_add(1, Ordering::Relaxed); let err_msg = e.to_string(); if let Ok(mut lock) = errors.lock() { - lock.push((path.clone(), err_msg)); + lock.push((path.clone(), err_msg.clone())); + } + if let Some(bar) = &pb { + bar.println(format!("Failed: {} -> {}", path.display(), err_msg)); } } - if args.verbose || count.is_multiple_of(500) { - eprint!("\rProcessed: {}/{}", count, files.len()); + if let Some(bar) = &pb { + bar.inc(1); } }); - if args.verbose { eprintln!(); } - - let success = processed.load(Ordering::Relaxed) - failed.load(Ordering::Relaxed); + let success = files.len() - failed.load(Ordering::Relaxed); println!("\nSuccessful: {} | Failure: {}", success, failed.load(Ordering::Relaxed)); if failed.load(Ordering::Relaxed) > 0 { println!("\nFailed:"); for (path, err) in errors.lock().unwrap().iter() { - println!(" Failed: {}: {}", path.display(), err); + println!(" {}: {}", path.display(), err); } std::process::exit(1); } From 5a680ede8c245bd7670550220cd07a269ff02bda Mon Sep 17 00:00:00 2001 From: VeryBaaad Date: Wed, 22 Apr 2026 01:22:23 +0800 Subject: [PATCH 2/2] fix: ensure progress bar completes after parallel iteration --- src/main.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.rs b/src/main.rs index da25b8c..8d42eda 100644 --- a/src/main.rs +++ b/src/main.rs @@ -122,6 +122,10 @@ fn main() -> Result<()> { } }); + if let Some(bar) = &pb { + bar.finish(); + } + let success = files.len() - failed.load(Ordering::Relaxed); println!("\nSuccessful: {} | Failure: {}", success, failed.load(Ordering::Relaxed));