use std::env;
use std::sync::atomic::{AtomicBool, Ordering};
static DEBUG_ENABLED: AtomicBool = AtomicBool::new(false);
static INITIALIZED: AtomicBool = AtomicBool::new(false);
pub fn init() {
if INITIALIZED.swap(true, Ordering::SeqCst) {
return; }
let enabled = env::var("FASTAPI_OUTPUT_DEBUG")
.map(|v| v == "1" || v.to_lowercase() == "true")
.unwrap_or(false);
DEBUG_ENABLED.store(enabled, Ordering::SeqCst);
if enabled {
eprintln!("[FASTAPI_OUTPUT] Debug logging enabled");
}
}
#[must_use]
pub fn is_debug_enabled() -> bool {
if !INITIALIZED.load(Ordering::SeqCst) {
init();
}
DEBUG_ENABLED.load(Ordering::SeqCst)
}
pub fn enable_debug() {
INITIALIZED.store(true, Ordering::SeqCst);
DEBUG_ENABLED.store(true, Ordering::SeqCst);
}
pub fn disable_debug() {
DEBUG_ENABLED.store(false, Ordering::SeqCst);
}
#[doc(hidden)]
pub fn reset_for_test() {
INITIALIZED.store(false, Ordering::SeqCst);
DEBUG_ENABLED.store(false, Ordering::SeqCst);
}
#[macro_export]
macro_rules! debug_log {
($($arg:tt)*) => {
if $crate::debug::is_debug_enabled() {
eprintln!("[FASTAPI_OUTPUT] {}", format!($($arg)*));
}
};
}
#[macro_export]
macro_rules! debug_detection {
($($arg:tt)*) => {
if $crate::debug::is_debug_enabled() {
eprintln!("[FASTAPI_OUTPUT:DETECT] {}", format!($($arg)*));
}
};
}
#[macro_export]
macro_rules! debug_mode {
($($arg:tt)*) => {
if $crate::debug::is_debug_enabled() {
eprintln!("[FASTAPI_OUTPUT:MODE] {}", format!($($arg)*));
}
};
}
#[macro_export]
macro_rules! debug_render {
($($arg:tt)*) => {
if $crate::debug::is_debug_enabled() {
eprintln!("[FASTAPI_OUTPUT:RENDER] {}", format!($($arg)*));
}
};
}
#[macro_export]
macro_rules! debug_test {
($($arg:tt)*) => {
if $crate::debug::is_debug_enabled() {
eprintln!("[FASTAPI_OUTPUT:TEST] {}", format!($($arg)*));
}
};
}
#[macro_export]
macro_rules! debug_component {
($($arg:tt)*) => {
if $crate::debug::is_debug_enabled() {
eprintln!("[FASTAPI_OUTPUT:COMPONENT] {}", format!($($arg)*));
}
};
}
#[cfg(test)]
mod tests {
use super::*;
fn setup() {
reset_for_test();
}
#[test]
fn test_debug_disabled_by_default() {
setup();
assert!(!DEBUG_ENABLED.load(Ordering::SeqCst));
}
#[test]
fn test_enable_disable_debug() {
setup();
assert!(!is_debug_enabled());
enable_debug();
assert!(is_debug_enabled());
disable_debug();
assert!(!is_debug_enabled());
}
#[test]
fn test_debug_log_macro_when_disabled() {
setup();
disable_debug();
debug_log!("This should not appear");
}
#[test]
fn test_debug_log_macro_when_enabled() {
setup();
enable_debug();
debug_log!("Test message: {}", 42);
disable_debug();
}
#[test]
fn test_all_debug_macros() {
setup();
enable_debug();
debug_log!("general log test");
debug_detection!("detection test");
debug_mode!("mode test");
debug_render!("render test");
debug_test!("test test");
debug_component!("component test");
disable_debug();
}
#[test]
fn test_init_idempotent() {
setup();
init();
assert!(INITIALIZED.load(Ordering::SeqCst));
init();
assert!(INITIALIZED.load(Ordering::SeqCst));
}
#[test]
fn test_is_debug_enabled_auto_initializes() {
setup();
assert!(!INITIALIZED.load(Ordering::SeqCst));
let _ = is_debug_enabled();
assert!(INITIALIZED.load(Ordering::SeqCst));
}
#[test]
fn test_enable_debug_marks_initialized() {
setup();
assert!(!INITIALIZED.load(Ordering::SeqCst));
enable_debug();
assert!(INITIALIZED.load(Ordering::SeqCst));
assert!(is_debug_enabled());
}
}