use std::path::PathBuf;
use padlock_core::findings::Report;
use crate::config::Config;
use crate::filter::{FailSeverity, FilterArgs};
use crate::paths::collect_layouts;
pub struct AnalyzeOpts {
pub json: bool,
pub sarif: bool,
pub markdown: bool,
pub cache_line_size: Option<usize>,
pub word_size: Option<usize>,
pub fail_on_severity: Option<FailSeverity>,
pub target: Option<String>,
pub stdlib: Option<padlock_source::CppStdlib>,
}
pub fn run(paths: &[PathBuf], opts: AnalyzeOpts, filter: &FilterArgs) -> anyhow::Result<()> {
let AnalyzeOpts {
json,
sarif,
markdown,
cache_line_size,
word_size,
fail_on_severity,
target,
stdlib,
} = opts;
if let Some(s) = stdlib {
padlock_source::set_cpp_stdlib(s);
}
let cfg = Config::for_path(
paths
.first()
.map(|p| p.as_path())
.unwrap_or(std::path::Path::new(".")),
);
let mut filter = filter.clone();
filter.apply_config_defaults(&cfg);
let (mut layouts, analyzed, skipped) = collect_layouts(paths)?;
let arch_name_override = target.as_deref().or(cfg.arch_override.as_deref());
if let Some(arch_name) = arch_name_override {
let arch = padlock_core::arch::arch_by_name(arch_name).unwrap_or_else(|| {
eprintln!("padlock: warning: unknown target/arch '{arch_name}', ignoring override");
padlock_dwarf::reader::detect_arch_from_host()
});
for layout in &mut layouts {
layout.arch = arch;
}
}
if cache_line_size.is_some() || word_size.is_some() {
for layout in &mut layouts {
layout.arch =
padlock_core::arch::with_overrides(layout.arch, cache_line_size, word_size);
}
}
if !cfg.custom_sync_types.is_empty() {
for layout in &mut layouts {
padlock_source::concurrency::annotate_custom_types(layout, &cfg.custom_sync_types);
}
}
layouts.retain(|l| {
!cfg.is_ignored(&l.name)
&& !l
.source_file
.as_deref()
.map(|f| cfg.is_path_excluded(f))
.unwrap_or(false)
});
filter.apply_to_layouts(&mut layouts)?;
let mut report = Report::from_layouts(&layouts);
report.analyzed_paths = analyzed;
report.skipped = skipped;
for sr in &mut report.structs {
sr.findings
.retain(|f| cfg.should_report_for(&sr.struct_name, f.severity()));
}
filter.apply_to_report(&mut report);
let score_failed = report.structs.iter().any(|s| {
let threshold = cfg.fail_below_for(&s.struct_name);
threshold > 0 && s.score < threshold as f64
});
let effective_fail_sev: Option<FailSeverity> = fail_on_severity.or_else(|| {
cfg.fail_on_severity.as_ref().map(|s| match s {
padlock_core::findings::Severity::High => FailSeverity::High,
padlock_core::findings::Severity::Medium => FailSeverity::Medium,
padlock_core::findings::Severity::Low => FailSeverity::Low,
})
});
let severity_failed = if let Some(ref threshold) = effective_fail_sev {
report
.structs
.iter()
.flat_map(|s| &s.findings)
.any(|f| threshold.matches(f.severity()))
} else {
false
};
let failed = score_failed || severity_failed;
if sarif {
println!("{}", padlock_output::to_sarif(&report)?);
} else if json {
println!("{}", padlock_output::to_json(&report)?);
} else if markdown {
print!("{}", padlock_output::to_markdown(&report));
} else {
print!("{}", padlock_output::render_report(&report));
}
if failed {
std::process::exit(1);
}
Ok(())
}