use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use crate::nodes::node::Node;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DebugLevel {
None = 0,
Error = 1,
Warn = 2,
Info = 3,
Debug = 4,
Trace = 5,
}
#[derive(Debug, Clone)]
pub struct DebugContext {
level: DebugLevel,
logs: Vec<String>,
breakpoints: Vec<String>,
}
impl DebugContext {
pub fn new(level: DebugLevel) -> Self {
Self {
level,
logs: Vec::new(),
breakpoints: Vec::new(),
}
}
pub fn with_info() -> Self {
Self::new(DebugLevel::Info)
}
pub fn with_debug() -> Self {
Self::new(DebugLevel::Debug)
}
pub fn with_trace() -> Self {
Self::new(DebugLevel::Trace)
}
pub fn set_level(&mut self, level: DebugLevel) {
self.level = level;
}
pub fn level(&self) -> DebugLevel {
self.level
}
pub fn log(&mut self, level: DebugLevel, message: String) {
if level <= self.level {
self.logs.push(format!("[{:?}] {}", level, message));
}
}
pub fn error(&mut self, message: String) {
self.log(DebugLevel::Error, message);
}
pub fn warn(&mut self, message: String) {
self.log(DebugLevel::Warn, message);
}
pub fn info(&mut self, message: String) {
self.log(DebugLevel::Info, message);
}
pub fn debug(&mut self, message: String) {
self.log(DebugLevel::Debug, message);
}
pub fn trace(&mut self, message: String) {
self.log(DebugLevel::Trace, message);
}
pub fn add_breakpoint(&mut self, name: String) {
self.breakpoints.push(name);
}
pub fn has_breakpoint(&self, name: &str) -> bool {
self.breakpoints.iter().any(|bp| bp == name)
}
pub fn logs(&self) -> &[String] {
&self.logs
}
pub fn breakpoints(&self) -> &[String] {
&self.breakpoints
}
pub fn clear_logs(&mut self) {
self.logs.clear();
}
pub fn clear_breakpoints(&mut self) {
self.breakpoints.clear();
}
pub fn format_logs(&self) -> String {
self.logs.join("\n")
}
}
impl Default for DebugContext {
fn default() -> Self {
Self::new(DebugLevel::Info)
}
}
pub struct NodeDebugger {
context: DebugContext,
trace_enabled: bool,
}
impl NodeDebugger {
pub fn new() -> Self {
Self {
context: DebugContext::with_debug(),
trace_enabled: false,
}
}
pub fn with_trace() -> Self {
Self {
context: DebugContext::with_trace(),
trace_enabled: true,
}
}
pub fn enable_trace(&mut self) {
self.trace_enabled = true;
self.context.set_level(DebugLevel::Trace);
}
pub fn disable_trace(&mut self) {
self.trace_enabled = false;
}
pub fn context(&self) -> &DebugContext {
&self.context
}
pub fn context_mut(&mut self) -> &mut DebugContext {
&mut self.context
}
pub fn debug_access(&mut self, path: &str, node: &Node) {
let msg = format!(
"Access: {} -> {:?}",
path,
crate::devtools::inspect::node_type(node)
);
self.context.debug(msg);
}
pub fn debug_create(&mut self, node: &Node) {
let msg = format!("Create: {:?}", crate::devtools::inspect::node_type(node));
self.context.debug(msg);
}
pub fn debug_modify(&mut self, path: &str, old: &Node, new: &Node) {
let msg = format!(
"Modify: {} from {:?} to {:?}",
path,
crate::devtools::inspect::node_type(old),
crate::devtools::inspect::node_type(new)
);
self.context.debug(msg);
}
pub fn trace_visit(&mut self, depth: usize, node: &Node) {
if self.trace_enabled {
let indent = " ".repeat(depth);
let msg = format!(
"{}Visit: {:?}",
indent,
crate::devtools::inspect::node_type(node)
);
self.context.trace(msg);
}
}
pub fn logs(&self) -> String {
self.context.format_logs()
}
pub fn clear(&mut self) {
self.context.clear_logs();
self.context.clear_breakpoints();
}
}
impl Default for NodeDebugger {
fn default() -> Self {
Self::new()
}
}
pub struct DebugAssert;
impl DebugAssert {
pub fn assert_type(
node: &Node,
expected: crate::devtools::inspect::NodeType,
) -> Result<(), String> {
let actual = crate::devtools::inspect::node_type(node);
if actual == expected {
Ok(())
} else {
Err(format!(
"Type assertion failed: expected {:?}, got {:?}",
expected, actual
))
}
}
pub fn assert_size(node: &Node, expected: usize) -> Result<(), String> {
let actual = crate::devtools::inspect::node_size(node);
if actual == expected {
Ok(())
} else {
Err(format!(
"Size assertion failed: expected {}, got {}",
expected, actual
))
}
}
pub fn assert_max_depth(node: &Node, max_depth: usize) -> Result<(), String> {
let actual = crate::devtools::inspect::node_depth(node);
if actual <= max_depth {
Ok(())
} else {
Err(format!(
"Depth assertion failed: expected <= {}, got {}",
max_depth, actual
))
}
}
pub fn assert_scalar(node: &Node) -> Result<(), String> {
let node_type = crate::devtools::inspect::node_type(node);
if node_type.is_scalar() {
Ok(())
} else {
Err(format!("Scalar assertion failed: node is {:?}", node_type))
}
}
pub fn assert_collection(node: &Node) -> Result<(), String> {
let node_type = crate::devtools::inspect::node_type(node);
if node_type.is_collection() {
Ok(())
} else {
Err(format!(
"Collection assertion failed: node is {:?}",
node_type
))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_debug_level_ordering() {
assert!(DebugLevel::Error < DebugLevel::Warn);
assert!(DebugLevel::Warn < DebugLevel::Info);
assert!(DebugLevel::Info < DebugLevel::Debug);
assert!(DebugLevel::Debug < DebugLevel::Trace);
}
#[test]
fn test_debug_context() {
let mut ctx = DebugContext::new(DebugLevel::Info);
ctx.error("Error message".to_string());
ctx.info("Info message".to_string());
ctx.debug("Debug message".to_string());
let logs = ctx.logs();
assert_eq!(logs.len(), 2); assert!(logs[0].contains("Error"));
assert!(logs[1].contains("Info"));
}
#[test]
fn test_breakpoints() {
let mut ctx = DebugContext::new(DebugLevel::Debug);
ctx.add_breakpoint("parse_start".to_string());
ctx.add_breakpoint("parse_end".to_string());
assert!(ctx.has_breakpoint("parse_start"));
assert!(ctx.has_breakpoint("parse_end"));
assert!(!ctx.has_breakpoint("unknown"));
ctx.clear_breakpoints();
assert!(!ctx.has_breakpoint("parse_start"));
}
#[test]
fn test_node_debugger() {
let mut debugger = NodeDebugger::new();
let node = Node::from("test");
debugger.debug_create(&node);
debugger.debug_access("/path/to/node", &node);
let logs = debugger.logs();
assert!(logs.contains("Create"));
assert!(logs.contains("Access"));
}
#[test]
fn test_debug_assert_type() {
use crate::devtools::inspect::NodeType;
let node = Node::from("test");
assert!(DebugAssert::assert_type(&node, NodeType::String).is_ok());
assert!(DebugAssert::assert_type(&node, NodeType::Integer).is_err());
}
#[test]
fn test_debug_assert_size() {
let node = Node::Array(vec![Node::from(1), Node::from(2)]);
assert!(DebugAssert::assert_size(&node, 2).is_ok());
assert!(DebugAssert::assert_size(&node, 3).is_err());
}
#[test]
fn test_debug_assert_scalar() {
assert!(DebugAssert::assert_scalar(&Node::from("test")).is_ok());
assert!(DebugAssert::assert_scalar(&Node::Array(vec![])).is_err());
}
#[test]
fn test_trace_enabled() {
let mut debugger = NodeDebugger::with_trace();
assert!(debugger.trace_enabled);
debugger.disable_trace();
assert!(!debugger.trace_enabled);
}
#[test]
fn test_debug_context_log_levels() {
let mut ctx = DebugContext::new(DebugLevel::Trace);
ctx.error("Error".to_string());
ctx.warn("Warn".to_string());
ctx.info("Info".to_string());
ctx.debug("Debug".to_string());
ctx.trace("Trace".to_string());
let logs = ctx.logs();
assert_eq!(logs.len(), 5);
assert!(logs[0].contains("Error"));
assert!(logs[1].contains("Warn"));
assert!(logs[2].contains("Info"));
assert!(logs[3].contains("Debug"));
assert!(logs[4].contains("Trace"));
}
#[test]
fn test_debug_context_clear_logs() {
let mut ctx = DebugContext::new(DebugLevel::Debug);
ctx.info("Info message".to_string());
ctx.debug("Debug message".to_string());
assert!(!ctx.logs().is_empty());
ctx.clear_logs();
assert!(ctx.logs().is_empty());
}
#[test]
fn test_node_debugger_trace_visit() {
let mut debugger = NodeDebugger::with_trace();
let node = Node::from("test");
debugger.trace_visit(2, &node);
let logs = debugger.logs();
assert!(logs.contains("Visit"));
assert!(logs.contains(" ")); }
#[test]
fn test_debug_assert_max_depth() {
let node = Node::Array(vec![Node::from(1), Node::Array(vec![Node::from(2)])]);
let depth = crate::devtools::inspect::node_depth(&node);
println!("Actual depth: {}", depth);
assert!(DebugAssert::assert_max_depth(&node, depth).is_ok());
assert!(DebugAssert::assert_max_depth(&node, depth - 1).is_err());
}
#[test]
fn test_debug_assert_collection() {
let node = Node::Array(vec![Node::from(1)]);
assert!(DebugAssert::assert_collection(&node).is_ok());
assert!(DebugAssert::assert_collection(&Node::from("test")).is_err());
}
}