use std::cell::RefCell;
use std::collections::VecDeque;
use std::thread_local;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InvariantType {
Correctness,
Safety,
Performance,
State,
}
impl Default for InvariantType {
fn default() -> Self {
Self::Correctness
}
}
#[derive(Debug, Clone)]
pub struct InvariantRecord {
pub message: String,
pub invariant_type: InvariantType,
pub context: String,
pub passed: bool,
}
thread_local! {
static INVARIANT_HISTORY: RefCell<VecDeque<InvariantRecord>> = RefCell::new(VecDeque::with_capacity(100));
}
#[macro_export]
macro_rules! assert_invariant {
($condition:expr, $message:expr) => {
$crate::invariant_ppt::__assert_invariant_impl($condition, $message, None, $crate::invariant_ppt::InvariantType::Correctness)
};
($condition:expr, $message:expr, $context:expr) => {
$crate::invariant_ppt::__assert_invariant_impl($condition, $message, Some($context), $crate::invariant_ppt::InvariantType::Correctness)
};
}
#[doc(hidden)]
pub fn __assert_invariant_impl(
condition: bool,
message: &str,
context: Option<&str>,
type_: InvariantType
) {
let ctx = context.unwrap_or("unknown");
let record = InvariantRecord {
message: message.to_string(),
invariant_type: type_,
context: ctx.to_string(),
passed: condition,
};
INVARIANT_HISTORY.with(|history| {
let mut h = history.borrow_mut();
if h.len() >= 100 {
h.pop_front();
}
h.push_back(record);
});
if !condition {
panic!("INVARIANT VIOLATION [{}]: {}", ctx, message);
}
}
pub fn contract_test(test_name: &str, required_invariants: &[&str]) {
let history = INVARIANT_HISTORY.with(|h| h.borrow().clone());
let mut missing: Vec<&str> = Vec::new();
for invariant in required_invariants {
let found = history.iter().any(|r| r.message == *invariant);
if !found {
missing.push(invariant);
}
}
if !missing.is_empty() {
panic!(
"CONTRACT FAILURE [{}]: The following invariants were not checked:\n - {}",
test_name,
missing.join("\n - ")
);
}
}
pub fn clear_invariant_log() {
INVARIANT_HISTORY.with(|h| {
h.borrow_mut().clear();
});
}
#[derive(Debug, Clone, PartialEq)]
pub struct PerfSnapshot {
pub label: String,
pub latency_ms: f64,
pub throughput_ops: f64,
pub memory_delta_kb: i64,
}
pub fn assert_performance_invariant(
snapshot: &PerfSnapshot,
baseline_latency: f64,
tolerance_factor: f64
) {
let max_latency = baseline_latency * (1.0 + tolerance_factor);
__assert_invariant_impl(
snapshot.latency_ms <= max_latency,
&format!("PERF: {} latency within predicted envelope", snapshot.label),
Some("performance"),
InvariantType::Performance
);
}