diff --git a/Cargo.lock b/Cargo.lock index 06e8f4a0..04fe52e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -514,6 +514,7 @@ dependencies = [ "rayon", "rusqlite", "serde", + "thiserror", "toml", "tracing", "tracing-subscriber", diff --git a/Cargo.toml b/Cargo.toml index b76ae97e..5c258dda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,4 @@ console = "0.15.11" rayon = "1.10.0" r2d2_sqlite = "0.30.0" r2d2 = "0.8.10" +thiserror = "2.0.12" diff --git a/src/commands/scan.rs b/src/commands/scan.rs index 1df0023e..d315ecd2 100644 --- a/src/commands/scan.rs +++ b/src/commands/scan.rs @@ -10,7 +10,7 @@ use crate::utils::config::Config; use crate::utils::query_cache; use crate::walk::spawn_senders; use rayon::prelude::*; - +use std::collections::BTreeMap; use tree_sitter::{Language, Parser, QueryCursor, StreamingIterator}; type DynError = Box; @@ -37,7 +37,7 @@ pub fn handle( let (project_name, db_path) = get_project_info(&scan_path, database_dir)?; let diags: Vec = if no_index { - scan_filesystem(&scan_path, config).unwrap() + scan_filesystem(&scan_path, config)? } else { if rebuild_index || !db_path.exists() { tracing::debug!("Scanning filesystem index filesystem"); @@ -49,24 +49,37 @@ pub fn handle( }; tracing::debug!("Found {:?} issues.", diags.len()); - - if format == "console" || (format.is_empty() && config.output.default_format == "console") { + + if format == "console" + || (format.is_empty() && config.output.default_format == "console") + { tracing::debug!("Printing to console"); + let mut grouped: BTreeMap<&str, Vec<&Diag>> = BTreeMap::new(); for d in &diags { - let sev_str = match d.severity { - Severity::High => style("HIGH").red().bold(), - Severity::Medium => style("MEDIUM").yellow().bold(), - Severity::Low => style("LOW").cyan().bold(), - }; - println!( - "{}:{}:{} [{}] {}", - style(d.path.clone()).blue().underlined(), - d.line, - d.col, - sev_str, - style(&d.id).bold(), - ); + grouped.entry(&d.path).or_default().push(d); } + + for (path, issues) in grouped { + println!("{}", style(path).blue().underlined()); + for d in issues { + let sev_str = match d.severity { + Severity::High => style("HIGH").red().bold(), + Severity::Medium => style("MEDIUM").yellow().bold(), + Severity::Low => style("LOW").cyan().bold(), + }; + println!( + " {:>4}:{:<4} [{}] {}", + d.line, d.col, sev_str, style(&d.id).bold() + ); + } + println!(); + } + + println!("{} '{}' generated {} issues.", + style("warning").yellow().bold(), + style(project_name).white().bold(), + style(diags.len()).bold()); + println!("\t"); // TODO: Add individual counts for different warning levels } Ok(()) } @@ -78,21 +91,20 @@ pub fn handle( fn scan_filesystem( root: &Path, cfg: &Config, -) ->Result, Box> { +) ->Result, Box> { let rx = spawn_senders(root, cfg); let acc = Mutex::new(Vec::new()); - + rx.into_iter() .flatten() .par_bridge() .try_for_each(|path| { let mut local = run_rules_on_file(&path, cfg).unwrap(); - let mut guard = acc.lock().unwrap(); - guard.append(&mut local); + acc.lock().unwrap().append(&mut local); Ok::<(), DynError>(()) - })?; + }).unwrap(); - Ok(acc.into_inner().unwrap()) + Ok(acc.into_inner()?) } fn scan_with_index_parallel( diff --git a/src/main.rs b/src/main.rs index 562f56fa..e5e8e25e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,8 @@ use cli::Cli; use clap::Parser; use directories::ProjectDirs; use std::fs; - +use std::time::Instant; +use console::style; use tracing_subscriber::{fmt, EnvFilter, Registry}; use tracing_subscriber::prelude::*; use tracing_subscriber::fmt::time; @@ -39,6 +40,7 @@ fn init_tracing() { } fn main() -> Result<(), Box> { + let now = Instant::now(); init_tracing(); tracing::debug!("CLI starting up"); @@ -57,6 +59,10 @@ fn main() -> Result<(), Box> { commands::handle_command(cli.command, database_dir, &mut config)?; + let elapsed = now.elapsed().as_millis(); + println!("{} in {} ms.", + style("Finished").green().bold(), + style(elapsed).white().bold()); Ok(()) } diff --git a/src/walk.rs b/src/walk.rs index b0600029..e4763b99 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -72,13 +72,13 @@ pub fn spawn_senders( .threads(worker_thrs) .overrides(overrides) .build_parallel(); - + walker.run(move || { let mut batcher = Batcher { tx: tx.clone(), batch: Vec::with_capacity(BATCH_SIZE), }; - + Box::new(move |entry| { tracing::debug!("walking: {:?}", entry); let e = match entry {