#[cfg(debug_assertions)]
use std::collections::HashMap;
#[cfg(debug_assertions)]
use std::sync::atomic::AtomicU64;
use std::sync::{
Mutex, OnceLock,
atomic::{AtomicBool, Ordering},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum ValidationSeverity {
Verbose = 0,
Info = 1,
Warning = 2,
Error = 3,
Fatal = 4,
}
impl ValidationSeverity {
pub fn as_str(self) -> &'static str {
match self {
Self::Verbose => "VERBOSE",
Self::Info => "INFO",
Self::Warning => "WARNING",
Self::Error => "ERROR",
Self::Fatal => "FATAL",
}
}
}
pub type ValidationCallback = fn(ValidationSeverity, &str, &str);
struct ValidationState {
enabled: AtomicBool,
min_severity: Mutex<ValidationSeverity>,
callback: Mutex<Option<ValidationCallback>>,
#[cfg(debug_assertions)]
counters: Mutex<HashMap<String, AtomicU64>>,
}
static STATE: OnceLock<ValidationState> = OnceLock::new();
fn state() -> &'static ValidationState {
STATE.get_or_init(|| ValidationState {
#[cfg(debug_assertions)]
enabled: AtomicBool::new(true),
#[cfg(not(debug_assertions))]
enabled: AtomicBool::new(false),
min_severity: Mutex::new(ValidationSeverity::Verbose),
callback: Mutex::new(None),
#[cfg(debug_assertions)]
counters: Mutex::new(HashMap::new()),
})
}
pub struct Validation;
impl Validation {
pub fn enable(on: bool) {
state().enabled.store(on, Ordering::Release);
}
pub fn is_enabled() -> bool {
state().enabled.load(Ordering::Acquire)
}
pub fn init_from_env() {
if std::env::var("OA_VK_VALIDATION")
.map(|v| !matches!(v.as_str(), "" | "0" | "false" | "no" | "off"))
.unwrap_or(false)
{
Self::enable(true);
}
if let Ok(sev) = std::env::var("OA_VALIDATION_SEVERITY") {
let parsed = match sev.to_ascii_uppercase().as_str() {
"VERBOSE" => Some(ValidationSeverity::Verbose),
"INFO" => Some(ValidationSeverity::Info),
"WARNING" | "WARN" => Some(ValidationSeverity::Warning),
"ERROR" => Some(ValidationSeverity::Error),
"FATAL" => Some(ValidationSeverity::Fatal),
_ => None,
};
if let Some(s) = parsed {
Self::set_min_severity(s);
}
}
}
pub fn set_min_severity(sev: ValidationSeverity) {
*state().min_severity.lock().unwrap() = sev;
}
pub fn get_min_severity() -> ValidationSeverity {
*state().min_severity.lock().unwrap()
}
pub fn set_callback(cb: Option<ValidationCallback>) {
*state().callback.lock().unwrap() = cb;
}
pub fn report(sev: ValidationSeverity, component: &str, message: &str) -> crate::Result<()> {
let s = state();
if !s.enabled.load(Ordering::Acquire) {
return Ok(());
}
if sev < *s.min_severity.lock().unwrap() {
return Ok(());
}
let cb = *s.callback.lock().unwrap();
if let Some(f) = cb {
f(sev, component, message);
} else {
eprintln!("[OA {}] {}: {}", sev.as_str(), component, message);
}
if sev >= ValidationSeverity::Error {
return Err(crate::core::error::Error::invalid_argument(format!(
"validation error [{component}]: {message}"
)));
}
Ok(())
}
#[cfg(debug_assertions)]
pub fn incr_counter(name: &str) {
let s = state();
let mut map = s.counters.lock().unwrap();
map
.entry(name.to_owned())
.or_insert_with(|| AtomicU64::new(0))
.fetch_add(1, Ordering::Relaxed);
}
#[cfg(not(debug_assertions))]
pub fn incr_counter(_name: &str) {}
#[cfg(debug_assertions)]
pub fn get_counter(name: &str) -> u64 {
state()
.counters
.lock()
.unwrap()
.get(name)
.map(|c| c.load(Ordering::Relaxed))
.unwrap_or(0)
}
#[cfg(not(debug_assertions))]
pub fn get_counter(_name: &str) -> u64 {
0
}
#[cfg(debug_assertions)]
pub fn reset_counters() {
state().counters.lock().unwrap().clear();
}
#[cfg(not(debug_assertions))]
pub fn reset_counters() {}
}
#[macro_export]
macro_rules! oa_validate {
($cond:expr, $sev:expr, $comp:expr, $($arg:tt)*) => {
#[cfg(any(debug_assertions, feature = "oa-enable-validation"))]
{
if $crate::Validation::is_enabled() && !($cond) {
let msg = format!($($arg)*);
$crate::Validation::report($sev, $comp, &msg)?;
}
}
};
}
#[macro_export]
macro_rules! oa_warn_perf {
($cond:expr, $($arg:tt)*) => {
#[cfg(any(debug_assertions, feature = "oa-enable-validation"))]
{
if $crate::Validation::is_enabled() && ($cond) {
let msg = format!($($arg)*);
let _ = $crate::Validation::report(
$crate::ValidationSeverity::Warning, "Core", &msg);
}
}
};
}