use std::{
panic::Location,
sync::{
LazyLock,
atomic::{self, AtomicUsize},
},
};
use crate::dispatch::{self, Event, dispatcher};
#[cfg(feature = "enabled")]
#[linkme::distributed_slice]
pub static PRECEPT_CATALOG: [CatalogEntry];
#[cfg(not(feature = "enabled"))]
pub static PRECEPT_CATALOG: [&CatalogEntry; 0] = [];
pub(crate) fn init_catalog() {
let dispatch = dispatcher();
for entry in PRECEPT_CATALOG {
dispatch.emit(Event::RegisterEntry(entry));
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Expectation {
Always,
AlwaysOrUnreachable,
Sometimes,
Reachable,
Unreachable,
}
impl Expectation {
pub fn check(self, condition: bool) -> bool {
use Expectation::*;
match (self, condition) {
(Always | AlwaysOrUnreachable, out) => out,
(Sometimes | Reachable, _) => true,
(Unreachable, _) => false,
}
}
}
#[derive(Debug)]
pub struct CatalogEntry {
expectation: Expectation,
property: &'static str,
location: &'static Location<'static>,
module: &'static str,
function: &'static LazyLock<&'static str>,
pass_count: AtomicUsize,
fail_count: AtomicUsize,
}
impl CatalogEntry {
pub const fn new(
expectation: Expectation,
property: &'static str,
location: &'static Location<'static>,
module: &'static str,
function: &'static LazyLock<&'static str>,
) -> Self {
Self {
expectation,
property,
location,
module,
function,
pass_count: AtomicUsize::new(0),
fail_count: AtomicUsize::new(0),
}
}
pub fn emit(&'static self, condition: bool, details: serde_json::Value) {
let count = if condition {
self.pass_count.fetch_add(1, atomic::Ordering::AcqRel)
} else {
self.fail_count.fetch_add(1, atomic::Ordering::AcqRel)
};
if count == 0 {
dispatch::emit(Event::EmitEntry { entry: self, condition, details });
}
}
#[inline]
pub fn expectation(&self) -> Expectation {
self.expectation
}
#[inline]
pub fn property(&self) -> &'static str {
self.property
}
#[inline]
pub fn location(&self) -> &'static Location<'static> {
self.location
}
#[inline]
pub fn module(&self) -> &'static str {
self.module
}
#[inline]
pub fn function(&self) -> &'static str {
self.function
}
pub fn pass_count(&self) -> usize {
self.pass_count.load(atomic::Ordering::Acquire)
}
pub fn fail_count(&self) -> usize {
self.fail_count.load(atomic::Ordering::Acquire)
}
}