pub mod cfg;
pub(crate) mod diags;
mod helpers;
use crate::{diag, LintLevel};
pub use diags::Code;
pub use helpers::{
db::{AdvisoryDb, DbSet, Fetch, Id, Report},
index::{Entry, Indices},
};
pub trait AuditReporter {
fn report(&mut self, report: serde_json::Value);
}
pub struct NoneReporter;
impl AuditReporter for NoneReporter {
fn report(&mut self, _report: serde_json::Value) {}
}
impl<F> AuditReporter for F
where
F: FnMut(serde_json::Value),
{
fn report(&mut self, report: serde_json::Value) {
self(report);
}
}
pub fn check<R, S>(
ctx: crate::CheckCtx<'_, cfg::ValidConfig>,
advisory_dbs: &DbSet,
audit_compatible_reporter: Option<R>,
indices: Option<Indices<'_>>,
sink: S,
) where
R: AuditReporter,
S: Into<diag::ErrorSink>,
{
let mut sink = sink.into();
let emit_audit_compatible_reports = audit_compatible_reporter.is_some();
let (report, yanked) = rayon::join(
|| Report::generate(advisory_dbs, ctx.krates, emit_audit_compatible_reports),
|| {
if let Some(indices) = indices {
let yanked: Vec<_> = ctx
.krates
.krates()
.filter_map(|package| match indices.is_yanked(package) {
Ok(is_yanked) => {
if is_yanked {
Some((package, None))
} else {
None
}
}
Err(err) => Some((package, Some(err))),
})
.collect();
yanked
} else {
Vec::new()
}
},
);
use bitvec::prelude::*;
let mut ignore_hits: BitVec = BitVec::repeat(false, ctx.cfg.ignore.len());
let mut ignore_yanked_hits: BitVec = BitVec::repeat(false, ctx.cfg.ignore_yanked.len());
for (krate, krate_index, advisory) in &report.advisories {
let diag = ctx.diag_for_advisory(
krate,
*krate_index,
&advisory.metadata,
Some(&advisory.versions),
|index| {
ignore_hits.as_mut_bitslice().set(index, true);
},
);
sink.push(diag);
}
for (krate, status) in yanked {
let Some(ind) = ctx.krates.nid_for_kid(&krate.id) else {
log::warn!("failed to locate node id for '{krate}'");
continue;
};
if let Some(e) = status {
if ctx.cfg.yanked.value != LintLevel::Allow {
sink.push(ctx.diag_for_index_failure(krate, ind, e));
}
} else {
if let Some(i) = ctx
.cfg
.ignore_yanked
.iter()
.position(|iy| crate::match_krate(krate, &iy.spec))
{
sink.push(ctx.diag_for_yanked_ignore(krate, i));
ignore_yanked_hits.as_mut_bitslice().set(i, true);
} else {
sink.push(ctx.diag_for_yanked(krate, ind));
}
}
}
for ignored in &ctx.cfg.ignore {
if !advisory_dbs.has_advisory(&ignored.id.value) {
sink.push(ctx.diag_for_unknown_advisory(ignored));
}
}
for ignore in ignore_hits
.into_iter()
.zip(ctx.cfg.ignore.iter())
.filter_map(|(hit, ignore)| if !hit { Some(ignore) } else { None })
{
sink.push(ctx.diag_for_advisory_not_encountered(ignore));
}
for ignore in ignore_yanked_hits
.into_iter()
.zip(ctx.cfg.ignore_yanked.iter())
.filter_map(|(hit, ignore)| if !hit { Some(ignore) } else { None })
{
sink.push(ctx.diag_for_ignored_yanked_not_encountered(ignore));
}
if let Some(mut reporter) = audit_compatible_reporter {
for ser_report in report.serialized_reports {
reporter.report(ser_report);
}
}
}