use alloc::vec::Vec;
use core::sync::atomic::{AtomicU64, Ordering};
#[cfg(debug_assertions)]
use super::verify_chain;
use super::{WitnessChecks, WitnessError};
use crate::diag::Diagnostic;
use crate::fact::FactView;
static UNVERIFIABLE: AtomicU64 = AtomicU64::new(0);
#[must_use]
pub fn unverifiable_witnesses() -> u64 {
UNVERIFIABLE.load(Ordering::Relaxed)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WitnessFailure {
pub diagnostic: usize,
pub error: WitnessError,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct AuditReport {
pub observed: usize,
pub verified: usize,
pub exempt: usize,
pub failures: Vec<WitnessFailure>,
}
#[derive(Debug, Default)]
pub struct WitnessAudit {
#[cfg(debug_assertions)]
report: AuditReport,
}
#[cfg(not(debug_assertions))]
const _: () = assert!(size_of::<WitnessAudit>() == 0);
impl WitnessAudit {
#[must_use]
pub const fn new() -> Self {
Self {
#[cfg(debug_assertions)]
report: AuditReport {
observed: 0,
verified: 0,
exempt: 0,
failures: Vec::new(),
},
}
}
#[cfg_attr(
not(debug_assertions),
expect(unused_variables, reason = "the audit only runs in debug builds")
)]
pub fn audit(
&mut self,
diagnostics: &[Diagnostic],
facts: &FactView<'_>,
checks: &WitnessChecks,
) {
#[cfg(debug_assertions)]
for diagnostic in diagnostics {
let ordinal = self.report.observed;
self.report.observed += 1;
if diagnostic.exemption().is_some() {
self.report.exempt += 1;
}
let Some(chain) = diagnostic.witness_chain() else {
continue;
};
match verify_chain(chain, facts, checks) {
Ok(()) => self.report.verified += 1,
Err(error) => {
UNVERIFIABLE.fetch_add(1, Ordering::Relaxed);
self.report.failures.push(WitnessFailure {
diagnostic: ordinal,
error,
});
}
}
}
}
#[must_use]
pub fn report(&self) -> AuditReport {
#[cfg(debug_assertions)]
{
self.report.clone()
}
#[cfg(not(debug_assertions))]
{
AuditReport::default()
}
}
}