2025-06-16 16:46:22 +02:00
|
|
|
use std::fs;
|
2025-06-23 19:37:19 +02:00
|
|
|
use std::process::exit;
|
|
|
|
|
use bytesize::ByteSize;
|
|
|
|
|
use chrono::{DateTime, Local};
|
|
|
|
|
use console::style;
|
2025-06-16 16:46:22 +02:00
|
|
|
use crate::cli::IndexAction;
|
2025-06-17 17:42:41 +02:00
|
|
|
use crate::database::index::{Indexer, IssueRow};
|
|
|
|
|
use crate::patterns::Severity;
|
|
|
|
|
use crate::utils::Config;
|
2025-06-16 16:46:22 +02:00
|
|
|
use crate::utils::project::get_project_info;
|
2025-06-17 17:42:41 +02:00
|
|
|
use crate::walk::spawn_senders;
|
2025-06-17 20:45:33 +02:00
|
|
|
use rayon::prelude::*;
|
2025-06-16 16:46:22 +02:00
|
|
|
|
|
|
|
|
pub fn handle(
|
|
|
|
|
action: IndexAction,
|
|
|
|
|
database_dir: &std::path::Path,
|
2025-06-17 17:42:41 +02:00
|
|
|
config: &Config,
|
2025-06-16 16:46:22 +02:00
|
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
|
match action {
|
|
|
|
|
IndexAction::Build { path, force } => {
|
|
|
|
|
let build_path = std::path::Path::new(&path).canonicalize()?;
|
|
|
|
|
let (project_name, db_path) = get_project_info(&build_path, database_dir)?;
|
|
|
|
|
|
|
|
|
|
if force || !db_path.exists() {
|
2025-06-17 17:42:41 +02:00
|
|
|
build_index(&project_name, &build_path, &db_path, config)?;
|
2025-06-23 19:39:38 +02:00
|
|
|
println!("✔ {} {}", style("Index built:" ).green(), style(db_path.display()).white().bold());
|
2025-06-16 16:46:22 +02:00
|
|
|
} else {
|
2025-06-23 19:37:19 +02:00
|
|
|
println!("{} {}", style("↩ Index already exists").yellow(), style("(use --force to rebuild)").dim());
|
2025-06-16 16:46:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
IndexAction::Status { path } => {
|
|
|
|
|
let status_path = std::path::Path::new(&path).canonicalize()?;
|
|
|
|
|
let (project_name, db_path) = get_project_info(&status_path, database_dir)?;
|
|
|
|
|
|
2025-06-23 19:37:19 +02:00
|
|
|
println!("{}", style("Project status").blue().bold().underlined());
|
|
|
|
|
println!(" {:14} {}", style("Project"), style(&project_name).white().bold());
|
|
|
|
|
println!(" {:14} {}", style("Index path"), style(db_path.display()).underlined());
|
|
|
|
|
println!(" {:14} {}", style("Exists"), style(db_path.exists()).bold());
|
2025-06-16 16:46:22 +02:00
|
|
|
|
|
|
|
|
if db_path.exists() {
|
2025-06-23 19:37:19 +02:00
|
|
|
let meta = fs::metadata(&db_path)?;
|
|
|
|
|
let size = ByteSize::b(meta.len());
|
|
|
|
|
let mtime: DateTime<Local> = meta.modified()?.into();
|
|
|
|
|
println!(" {:14} {}", style("Size"), size);
|
|
|
|
|
println!(" {:14} {}", style("Modified"), mtime.format("%Y-%m-%d %H:%M:%S"));
|
2025-06-16 16:46:22 +02:00
|
|
|
}
|
2025-06-23 19:37:19 +02:00
|
|
|
|
|
|
|
|
exit(0);
|
2025-06-16 16:46:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn build_index(
|
2025-06-17 17:42:41 +02:00
|
|
|
project_name: &str,
|
|
|
|
|
project_path: &std::path::Path,
|
2025-06-16 16:46:22 +02:00
|
|
|
db_path: &std::path::Path,
|
2025-06-17 17:42:41 +02:00
|
|
|
config: &Config,
|
2025-06-16 16:46:22 +02:00
|
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
2025-06-17 17:42:41 +02:00
|
|
|
tracing::debug!("Building index for: {}", project_name);
|
2025-06-16 16:46:22 +02:00
|
|
|
fs::File::create(db_path)?;
|
2025-06-17 17:42:41 +02:00
|
|
|
|
2025-06-17 20:45:33 +02:00
|
|
|
let pool = Indexer::init(db_path)?;
|
|
|
|
|
{
|
2025-06-23 17:49:15 +02:00
|
|
|
let idx = Indexer::from_pool(project_name, &pool).unwrap();
|
2025-06-17 20:45:33 +02:00
|
|
|
idx.clear()?;
|
|
|
|
|
}
|
2025-06-17 17:42:41 +02:00
|
|
|
|
2025-06-17 20:45:33 +02:00
|
|
|
tracing::debug!("Cleaned index for: {}", project_name);
|
|
|
|
|
|
|
|
|
|
let rx = spawn_senders(project_path, config);
|
|
|
|
|
let paths: Vec<_> = rx.into_iter().flatten().collect();
|
|
|
|
|
|
|
|
|
|
paths.into_par_iter().try_for_each(|path| -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
let issues = crate::commands::scan::run_rules_on_file(&path, config).unwrap();
|
|
|
|
|
let mut idx = Indexer::from_pool(project_name, &pool).unwrap();
|
|
|
|
|
let file_id = idx.upsert_file(&path).unwrap();
|
2025-06-17 17:42:41 +02:00
|
|
|
|
2025-06-17 20:45:33 +02:00
|
|
|
let rows: Vec<IssueRow> = issues.iter().map(|d| IssueRow {
|
|
|
|
|
rule_id: d.id.as_ref(),
|
|
|
|
|
severity: match d.severity {
|
|
|
|
|
Severity::High => "HIGH",
|
|
|
|
|
Severity::Medium => "MEDIUM",
|
|
|
|
|
Severity::Low => "LOW",
|
|
|
|
|
},
|
|
|
|
|
line: d.line as i64,
|
|
|
|
|
col: d.col as i64,
|
|
|
|
|
}).collect();
|
|
|
|
|
|
|
|
|
|
idx.replace_issues(file_id, rows).unwrap();
|
|
|
|
|
Ok(())
|
|
|
|
|
}).unwrap();
|
2025-06-17 21:00:24 +02:00
|
|
|
|
|
|
|
|
{
|
2025-06-23 17:49:15 +02:00
|
|
|
let idx = Indexer::from_pool(project_name, &pool)?;
|
2025-06-17 21:00:24 +02:00
|
|
|
idx.vacuum()?;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 16:46:22 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|