Refactor error handling with NyxResult and enhance debugging

- Replaced `Result` with `NyxResult` across the codebase for consistent error management.
- Enhanced `NyxError` with new variants and utility conversions for better flexibility.
- Added detailed `tracing::debug` logs in `file.rs` and `walk.rs` for improved traceability.
- Simplified conditionals and improved path handling in `file.rs`.
- Refined severity filtering logic in `scan.rs`.
This commit is contained in:
elipeter 2025-06-23 20:59:49 +02:00
parent 0a66a0ae2d
commit bd788a8373
9 changed files with 81 additions and 43 deletions

View file

@ -1,12 +1,13 @@
use std::{env, fs};
use console::style;
use crate::errors::NyxResult;
use crate::utils::get_project_info;
pub fn handle(
project: Option<String>,
all: bool,
config_dir: &std::path::Path,
) -> Result<(), Box<dyn std::error::Error>> {
) -> NyxResult<()> {
if all {
println!("{}", style("Cleaning all indexes...").cyan().bold());
if config_dir.exists() {

View file

@ -10,12 +10,13 @@ use crate::utils::Config;
use crate::utils::project::get_project_info;
use crate::walk::spawn_senders;
use rayon::prelude::*;
use crate::errors::NyxResult;
pub fn handle(
action: IndexAction,
database_dir: &std::path::Path,
config: &Config,
) -> Result<(), Box<dyn std::error::Error>> {
) -> NyxResult<()> {
match action {
IndexAction::Build { path, force } => {
let build_path = std::path::Path::new(&path).canonicalize()?;
@ -57,13 +58,13 @@ pub fn build_index(
project_path: &std::path::Path,
db_path: &std::path::Path,
config: &Config,
) -> Result<(), Box<dyn std::error::Error>> {
) -> NyxResult<()> {
tracing::debug!("Building index for: {}", project_name);
fs::File::create(db_path)?;
let pool = Indexer::init(db_path)?;
{
let idx = Indexer::from_pool(project_name, &pool).unwrap();
let idx = Indexer::from_pool(project_name, &pool)?;
idx.clear()?;
}
@ -73,9 +74,9 @@ pub fn build_index(
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();
let issues = crate::commands::scan::run_rules_on_file(&path, config)?;
let mut idx = Indexer::from_pool(project_name, &pool)?;
let file_id = idx.upsert_file(&path)?;
let rows: Vec<IssueRow> = issues.iter().map(|d| IssueRow {
rule_id: d.id.as_ref(),
@ -88,9 +89,9 @@ pub fn build_index(
col: d.col as i64,
}).collect();
idx.replace_issues(file_id, rows).unwrap();
idx.replace_issues(file_id, rows)?;
Ok(())
}).unwrap();
})?;
{
let idx = Indexer::from_pool(project_name, &pool)?;

View file

@ -2,11 +2,12 @@ use std::fs;
use bytesize::ByteSize;
use chrono::{DateTime, Local};
use console::style;
use crate::errors::NyxResult;
pub fn handle(
verbose: bool,
database_dir: &std::path::Path,
) -> Result<(), Box<dyn std::error::Error>> {
) -> NyxResult<()> {
println!("{}", style("Indexed projects").blue().bold().underlined());
if !database_dir.exists() {

View file

@ -5,6 +5,7 @@ pub mod clean;
use crate::cli::Commands;
use std::path::Path;
use crate::errors::NyxResult;
use crate::patterns::Severity;
use crate::utils::config::Config;
@ -12,7 +13,7 @@ pub fn handle_command(
command: Commands,
database_dir: &Path,
config: &mut Config
) -> Result<(), Box<dyn std::error::Error>> {
) -> NyxResult<()> {
match command {
Commands::Scan { path, no_index, rebuild_index, format, high_only } => {
if high_only { config.scanner.min_severity = Severity::High };

View file

@ -33,7 +33,7 @@ pub fn handle(
format: String,
database_dir: &Path,
config: &Config,
) -> Result<(), Box<dyn std::error::Error>> {
) -> NyxResult<()> {
let scan_path = Path::new(path).canonicalize()?;
let (project_name, db_path) = get_project_info(&scan_path, database_dir)?;
@ -87,7 +87,7 @@ pub fn handle(
fn scan_filesystem(
root: &Path,
cfg: &Config,
) ->Result<Vec<Diag>, Box<dyn std::error::Error>> {
) -> NyxResult<Vec<Diag>> {
let rx = spawn_senders(root, cfg);
let acc = Mutex::new(Vec::new());
@ -95,10 +95,10 @@ fn scan_filesystem(
.flatten()
.par_bridge()
.try_for_each(|path| {
let mut local = run_rules_on_file(&path, cfg).unwrap();
let mut local = run_rules_on_file(&path, cfg)?;
acc.lock().unwrap().append(&mut local);
Ok::<(), DynError>(())
}).unwrap();
})?;
Ok(acc.into_inner()?)
}
@ -126,7 +126,7 @@ pub fn scan_with_index_parallel(
let mut diags = if needs_scan {
let d = run_rules_on_file(&path, cfg).unwrap_or_default();
let file_id = idx.upsert_file(&path).unwrap();
let file_id = idx.upsert_file(&path).unwrap_or_default();
idx.replace_issues(
file_id,
d.iter().map(|d| IssueRow {

View file

@ -1,6 +1,9 @@
use std::fmt;
use std::sync::PoisonError;
use serde::de::StdError;
use thiserror::Error;
pub type NyxResult<T, E = NyxError> = core::result::Result<T, E>;
pub type NyxResult<T, E = NyxError> = Result<T, E>;
#[derive(Debug, Error)]
pub enum NyxError {
@ -19,6 +22,39 @@ pub enum NyxError {
#[error("time error: {0}")]
Time(#[from] std::time::SystemTimeError),
#[error("other: {0}")]
Other(String),
}
#[error("poisoned lock: {0}")]
Poison(String),
#[error(transparent)]
Other(#[from] Box<dyn StdError + Send + Sync + 'static>),
#[error("{0}")]
Msg(String),
}
impl<T> From<PoisonError<T>> for NyxError
where
T: fmt::Debug,
{
fn from(err: PoisonError<T>) -> Self {
NyxError::Poison(err.to_string())
}
}
impl From<&str> for NyxError {
fn from(s: &str) -> Self {
NyxError::Msg(s.to_owned())
}
}
impl From<String> for NyxError {
fn from(s: String) -> Self {
NyxError::Msg(s)
}
}
impl From<Box<dyn std::error::Error>> for NyxError {
fn from(err: Box<dyn std::error::Error>) -> Self {
NyxError::Msg(err.to_string())
}
}

View file

@ -14,6 +14,7 @@ pub(crate) fn run_rules_on_file(
path: &Path,
cfg: &Config,
) -> NyxResult<Vec<Diag>> {
tracing::debug!("Running rules on: {}", path.display());
let bytes = std::fs::read(path)?;
// Fast binary-file guard (skip if >1% NULs)
@ -21,22 +22,17 @@ pub(crate) fn run_rules_on_file(
return Ok(vec![]);
}
let lang_name = match lowercase_ext(path) {
Some(l) => l,
None => return Ok(vec![]),
};
let ts_lang = match lang_name {
"rs" => Language::from(tree_sitter_rust::LANGUAGE),
"c" => Language::from(tree_sitter_c::LANGUAGE),
"cpp" => Language::from(tree_sitter_cpp::LANGUAGE),
"java"=> Language::from(tree_sitter_java::LANGUAGE),
"go" => Language::from(tree_sitter_go::LANGUAGE),
"php" => Language::from(tree_sitter_php::LANGUAGE_PHP),
"py" => Language::from(tree_sitter_python::LANGUAGE),
"ts" => Language::from(tree_sitter_typescript::LANGUAGE_TYPESCRIPT),
"js" => Language::from(tree_sitter_javascript::LANGUAGE),
_ => return Ok(vec![]),
let (ts_lang, lang_slug) = match lowercase_ext(path) {
Some("rs") => (Language::from(tree_sitter_rust::LANGUAGE), "rust"),
Some("c") => (Language::from(tree_sitter_c::LANGUAGE), "c"),
Some("cpp") => (Language::from(tree_sitter_cpp::LANGUAGE), "cpp"),
Some("java")=> (Language::from(tree_sitter_java::LANGUAGE), "java"),
Some("go") => (Language::from(tree_sitter_go::LANGUAGE), "go"),
Some("php") => (Language::from(tree_sitter_php::LANGUAGE_PHP), "php"),
Some("py") => (Language::from(tree_sitter_python::LANGUAGE), "python"),
Some("ts") => (Language::from(tree_sitter_typescript::LANGUAGE_TYPESCRIPT), "typescript"),
Some("js") => (Language::from(tree_sitter_javascript::LANGUAGE), "javascript"),
_ => return Ok(vec![]),
};
let _tree = PARSER.with(|cell| {
@ -48,12 +44,12 @@ pub(crate) fn run_rules_on_file(
let root = _tree.root_node();
let compiled = query_cache::for_lang(lang_name, ts_lang);
let compiled = query_cache::for_lang(lang_slug, ts_lang);
let mut cursor = QueryCursor::new();
let mut out = Vec::new();
for cq in compiled.iter() {
if cfg.scanner.min_severity > cq.meta.severity {
if cfg.scanner.min_severity <= cq.meta.severity {
continue;
}
let mut matches = cursor.matches(&cq.query, root, &*bytes);
@ -69,6 +65,6 @@ pub(crate) fn run_rules_on_file(
});
}
}
}
}
Ok(out)
}

View file

@ -17,7 +17,7 @@ use console::style;
use tracing_subscriber::{fmt, EnvFilter, Registry};
use tracing_subscriber::prelude::*;
use tracing_subscriber::fmt::time;
use crate::errors::NyxResult;
// use tracing_appender::rolling::{RollingFileAppender, Rotation};
// use tracing_appender::non_blocking;
@ -41,7 +41,7 @@ fn init_tracing() {
.init();
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
fn main() -> NyxResult<()> {
let now = Instant::now();
init_tracing();

View file

@ -73,11 +73,12 @@ pub fn spawn_senders(root: &Path, cfg: &Config) -> Receiver<Batch> {
.build_parallel()
.run(move || {
let mut b = Batcher {
tx: tx.clone(),
tx: tx.clone(),
batch: Vec::with_capacity(DEFAULT_BATCH),
};
Box::new(move |entry| {
tracing::debug!("walking {:?}", entry);
let entry = match entry {
Ok(e) if e.file_type().map(|ft| ft.is_file()).unwrap_or(false) => e,
_ => return WalkState::Continue,
@ -93,7 +94,8 @@ pub fn spawn_senders(root: &Path, cfg: &Config) -> Receiver<Batch> {
_ => {}
}
}
tracing::debug!("sending {:?}", entry);
b.push(entry.into_path());
WalkState::Continue
})