use crate::logic::grammar::Segment;
use crate::logic::segment::SegmentRange;
use std::fmt::{self, Display};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DebugLevel {
None = 0,
Error = 1,
Warn = 2,
Info = 3,
Debug = 4,
Trace = 5,
}
impl Display for DebugLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DebugLevel::None => write!(f, "NONE"),
DebugLevel::Error => write!(f, "ERROR"),
DebugLevel::Warn => write!(f, "WARN"),
DebugLevel::Info => write!(f, "INFO"),
DebugLevel::Debug => write!(f, "DEBUG"),
DebugLevel::Trace => write!(f, "TRACE"),
}
}
}
pub struct DebugConfig {
pub level: DebugLevel,
pub input: Option<String>,
pub module_filters: Vec<String>,
}
impl Default for DebugConfig {
fn default() -> Self {
Self {
level: DebugLevel::None,
input: None,
module_filters: Vec::new(),
}
}
}
thread_local! {
static DEBUG_CONFIG: std::cell::RefCell<DebugConfig> = std::cell::RefCell::new(DebugConfig::default());
}
pub fn set_debug_level(level: DebugLevel) {
DEBUG_CONFIG.with(|config| {
config.borrow_mut().level = level;
});
}
pub fn set_debug_input(input: Option<String>) {
DEBUG_CONFIG.with(|config| {
config.borrow_mut().input = input;
});
}
pub fn add_module_filter(module: &str) {
DEBUG_CONFIG.with(|config| {
config.borrow_mut().module_filters.push(module.to_string());
});
}
pub fn clear_module_filters() {
DEBUG_CONFIG.with(|config| {
config.borrow_mut().module_filters.clear();
});
}
pub fn is_debug_enabled(level: DebugLevel, module: &str) -> bool {
DEBUG_CONFIG.with(|config| {
let config = config.borrow();
if config.level < level {
return false;
}
if config.module_filters.is_empty() {
return true;
}
config
.module_filters
.iter()
.any(|filter| module.contains(filter))
})
}
#[macro_export]
macro_rules! debug {
($level:expr, $module:expr, $($arg:tt)*) => {
if $crate::logic::debug::is_debug_enabled($level, $module) {
println!("[{}:{}] {}", $level, $module, format!($($arg)*));
}
};
}
#[macro_export]
macro_rules! debug_error {
($module:expr, $($arg:tt)*) => {
$crate::debug!($crate::logic::debug::DebugLevel::Error, $module, $($arg)*);
};
}
#[macro_export]
macro_rules! debug_warn {
($module:expr, $($arg:tt)*) => {
$crate::debug!($crate::logic::debug::DebugLevel::Warn, $module, $($arg)*);
};
}
#[macro_export]
macro_rules! debug_info {
($module:expr, $($arg:tt)*) => {
$crate::debug!($crate::logic::debug::DebugLevel::Info, $module, $($arg)*)
};
}
#[macro_export]
macro_rules! debug_debug {
($module:expr, $($arg:tt)*) => {
$crate::debug!($crate::logic::debug::DebugLevel::Debug, $module, $($arg)*);
};
}
#[macro_export]
macro_rules! debug_trace {
($module:expr, $($arg:tt)*) => {
$crate::debug!($crate::logic::debug::DebugLevel::Trace, $module, $($arg)*);
};
}
pub struct DebugUtils;
impl DebugUtils {
pub fn format_span(span: Option<&SegmentRange>, segments: &[Segment]) -> String {
match span {
Some(range) => {
let start_seg = segments.get(range.start);
let end_seg = segments.get(range.end);
match (start_seg, end_seg) {
(Some(start), Some(end)) => {
let start_byte = start.start;
let end_byte = end.end;
format!(
"segs[{}..{}] bytes[{}..{}]",
range.start, range.end, start_byte, end_byte
)
}
_ => format!(
"invalid segment range: segs[{}..{}]",
range.start, range.end
),
}
}
None => "no span".to_string(),
}
}
pub fn extract_span_text(span: &SegmentRange, segments: &[Segment]) -> String {
let mut result = String::new();
for idx in span.start..=span.end {
if let Some(seg) = segments.get(idx) {
result.push_str(&seg.text());
if idx < span.end {
result.push(' '); }
}
}
result
}
}
pub struct DebugContext {
pub operation: String,
pub data: std::collections::HashMap<String, String>,
}
impl DebugContext {
pub fn new(operation: &str) -> Self {
Self {
operation: operation.to_string(),
data: std::collections::HashMap::new(),
}
}
pub fn add(&mut self, key: &str, value: &str) {
self.data.insert(key.to_string(), value.to_string());
}
pub fn debug_dump(&self, level: DebugLevel, module: &str) {
if is_debug_enabled(level, module) {
println!("[{}:{}] === {} ===", level, module, self.operation);
for (key, value) in &self.data {
println!("[{}:{}] {}: {}", level, module, key, value);
}
println!("[{}:{}] === END {} ===", level, module, self.operation);
}
}
}