ux: show scan/fix progress

This commit is contained in:
2025-12-31 23:29:21 -05:00
parent 9bd14a790a
commit 64ab813ae6

View File

@@ -296,6 +296,10 @@ fn handle_scan(args: ScanArgs, common: &CommonArgs) -> Result<i32> {
return Ok(0); return Ok(0);
} }
if !config.report.json {
eprintln!("Scanning {} file(s)...", files.len());
}
let batch = run_scans(files, &config, &ruleset)?; let batch = run_scans(files, &config, &ruleset)?;
let scans = batch.scans; let scans = batch.scans;
let has_issues = scans.iter().any(|scan| !scan.issues.is_empty()); let has_issues = scans.iter().any(|scan| !scan.issues.is_empty());
@@ -372,6 +376,14 @@ fn handle_fix(args: FixArgs, common: &CommonArgs) -> Result<i32> {
return Ok(0); return Ok(0);
} }
if !config.report.json {
if args.dry_run {
eprintln!("Planning fixes for {} file(s)...", files.len());
} else {
eprintln!("Fixing {} file(s)...", files.len());
}
}
let (scans, fixes, errors) = run_fixes(files, &config, &ruleset, args.dry_run)?; let (scans, fixes, errors) = run_fixes(files, &config, &ruleset, args.dry_run)?;
let fix_failed = fixes.iter().any(|fix| fix.applied && !fix.success); let fix_failed = fixes.iter().any(|fix| fix.applied && !fix.success);
let has_issues = scans.iter().any(|scan| !scan.issues.is_empty()); let has_issues = scans.iter().any(|scan| !scan.issues.is_empty());
@@ -417,18 +429,27 @@ fn run_scans(files: Vec<PathBuf>, config: &Config, ruleset: &RuleSet) -> Result<
}; };
let errors = AtomicUsize::new(0); let errors = AtomicUsize::new(0);
let started = AtomicUsize::new(0);
let total = files.len();
let show_progress = !config.report.json;
let scans = if let Some(jobs) = jobs { let scans = if let Some(jobs) = jobs {
let pool = ThreadPoolBuilder::new().num_threads(jobs).build()?; let pool = ThreadPoolBuilder::new().num_threads(jobs).build()?;
pool.install(|| { pool.install(|| {
files files
.par_iter() .par_iter()
.filter_map(|path| match scan_file(path, config, ruleset) { .filter_map(|path| {
Ok(scan) => Some(scan), let idx = started.fetch_add(1, Ordering::SeqCst) + 1;
Err(err) => { if show_progress {
eprintln!("[ERROR] {}: {}", path.display(), err); eprintln!("[SCAN {}/{}] {}", idx, total, path.display());
errors.fetch_add(1, Ordering::SeqCst); }
None match scan_file(path, config, ruleset) {
Ok(scan) => Some(scan),
Err(err) => {
eprintln!("[ERROR] {}: {}", path.display(), err);
errors.fetch_add(1, Ordering::SeqCst);
None
}
} }
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
@@ -436,12 +457,18 @@ fn run_scans(files: Vec<PathBuf>, config: &Config, ruleset: &RuleSet) -> Result<
} else { } else {
files files
.iter() .iter()
.filter_map(|path| match scan_file(path, config, ruleset) { .filter_map(|path| {
Ok(scan) => Some(scan), let idx = started.fetch_add(1, Ordering::SeqCst) + 1;
Err(err) => { if show_progress {
eprintln!("[ERROR] {}: {}", path.display(), err); eprintln!("[SCAN {}/{}] {}", idx, total, path.display());
errors.fetch_add(1, Ordering::SeqCst); }
None match scan_file(path, config, ruleset) {
Ok(scan) => Some(scan),
Err(err) => {
eprintln!("[ERROR] {}: {}", path.display(), err);
errors.fetch_add(1, Ordering::SeqCst);
None
}
} }
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
@@ -482,8 +509,15 @@ fn process_fix_batch(
let mut scans = Vec::new(); let mut scans = Vec::new();
let mut fixes = Vec::new(); let mut fixes = Vec::new();
let mut errors = 0usize; let mut errors = 0usize;
let total = files.len();
let show_progress = !config.report.json;
let mut idx = 0usize;
for path in files { for path in files {
idx += 1;
if show_progress {
eprintln!("[FIX {}/{}] {}", idx, total, path.display());
}
let scan = match scan_file(&path, config, ruleset) { let scan = match scan_file(&path, config, ruleset) {
Ok(scan) => scan, Ok(scan) => scan,
Err(err) => { Err(err) => {
@@ -522,9 +556,16 @@ fn process_fix_batch_parallel(
dry_run: bool, dry_run: bool,
) -> Result<(Vec<ScanOutcome>, Vec<FixOutcome>, usize)> { ) -> Result<(Vec<ScanOutcome>, Vec<FixOutcome>, usize)> {
let errors = AtomicUsize::new(0); let errors = AtomicUsize::new(0);
let started = AtomicUsize::new(0);
let total = files.len();
let show_progress = !config.report.json;
let results = files let results = files
.par_iter() .par_iter()
.filter_map(|path| { .filter_map(|path| {
let idx = started.fetch_add(1, Ordering::SeqCst) + 1;
if show_progress {
eprintln!("[FIX {}/{}] {}", idx, total, path.display());
}
let scan = match scan_file(path, config, ruleset) { let scan = match scan_file(path, config, ruleset) {
Ok(scan) => scan, Ok(scan) => scan,
Err(err) => { Err(err) => {
@@ -560,6 +601,9 @@ fn process_fix_batch_parallel(
fn watch_scan(paths: Vec<PathBuf>, config: &Config, ruleset: &RuleSet) -> Result<()> { fn watch_scan(paths: Vec<PathBuf>, config: &Config, ruleset: &RuleSet) -> Result<()> {
println!("Watch mode enabled. Waiting for files to settle..."); println!("Watch mode enabled. Waiting for files to settle...");
watch::watch_paths(&paths, config, |path| { watch::watch_paths(&paths, config, |path| {
if !config.report.json {
eprintln!("[SCAN] {}", path.display());
}
match scan_file(&path, config, ruleset) { match scan_file(&path, config, ruleset) {
Ok(scan) => { Ok(scan) => {
println!("{}", render_scan_line(&scan)); println!("{}", render_scan_line(&scan));
@@ -574,6 +618,9 @@ fn watch_scan(paths: Vec<PathBuf>, config: &Config, ruleset: &RuleSet) -> Result
fn watch_fix(paths: Vec<PathBuf>, config: &Config, ruleset: &RuleSet, dry_run: bool) -> Result<()> { fn watch_fix(paths: Vec<PathBuf>, config: &Config, ruleset: &RuleSet, dry_run: bool) -> Result<()> {
println!("Watch mode enabled. Waiting for files to settle..."); println!("Watch mode enabled. Waiting for files to settle...");
watch::watch_paths(&paths, config, |path| { watch::watch_paths(&paths, config, |path| {
if !config.report.json {
eprintln!("[FIX] {}", path.display());
}
match scan_file(&path, config, ruleset) { match scan_file(&path, config, ruleset) {
Ok(scan) => { Ok(scan) => {
let plan = fix::planner::plan_fix(&scan.issues, config.repair.policy); let plan = fix::planner::plan_fix(&scan.issues, config.repair.policy);