use crate::cli::commands::{AuditCmd, AuditErrorsCmd, AuditSubCommand};
use std::env;
use std::path::PathBuf;
use xbp_analysis::adapters::config::load_from_root;
use xbp_analysis::adapters::fix::SafeFixApplier;
use xbp_analysis::adapters::output::render_report;
use xbp_analysis::application::engine::AnalysisEngine;
use xbp_analysis::domain::request::{AnalysisRequest, OutputFormat};
use xbp_analysis::domain::types::{LanguageId, Severity};
use xbp_analysis::ports::inbound::FixService;
pub async fn run_audit(cmd: AuditCmd) -> Result<(), String> {
match cmd.command {
AuditSubCommand::Errors(errors) => run_audit_errors(errors).await,
}
}
async fn run_audit_errors(cmd: AuditErrorsCmd) -> Result<(), String> {
let root = cmd
.path
.clone()
.unwrap_or_else(|| env::current_dir().unwrap_or_else(|_| PathBuf::from(".")));
let mut config = load_from_root(&root)?;
if !cmd.enable.is_empty() {
config.rules.enabled = cmd.enable.clone();
}
if !cmd.disable.is_empty() {
config.rules.disabled.extend(cmd.disable.clone());
}
let format = OutputFormat::parse(&cmd.format)
.ok_or_else(|| {
format!(
"unknown format `{}` (use terminal, json, sarif, github-actions)",
cmd.format
)
})?;
let mut request = AnalysisRequest::new(&root);
request.config = config;
request.format = format;
request.apply_fixes = cmd.fix;
request.workspace = cmd.workspace;
if let Some(lang) = cmd.language {
request.language_filter = Some(LanguageId::new(lang));
}
if !cmd.files.is_empty() {
let mut paths = Vec::new();
for p in &cmd.files {
let abs = if p.is_absolute() {
p.clone()
} else {
root.join(p)
};
if abs.is_dir() {
request.root = abs;
} else {
paths.push(abs);
}
}
request.paths = paths;
}
let engine = AnalysisEngine::new();
let report = engine.analyze(&request)?;
if cmd.fix {
let fixer = SafeFixApplier::default_fs();
let applied = fixer.apply_safe_fixes(&report.findings, &[])?;
if applied.is_empty() {
eprintln!("note: no safe autofixes available for these findings (by design)");
} else {
for a in applied {
eprintln!("fixed {}: {}", a.path.display(), a.description);
}
}
}
let rendered = render_report(&report, format)?;
println!("{rendered}");
if let Some(fail) = cmd.fail_on.as_deref().and_then(Severity::parse) {
let bad = report.findings.iter().any(|f| f.severity >= fail);
if bad {
return Err(format!(
"audit failed: {} finding(s) at or above severity `{fail}`",
report
.findings
.iter()
.filter(|f| f.severity >= fail)
.count()
));
}
}
Ok(())
}