use super::table::{DebugTableBuilder, DebugTableOverlay};
#[derive(Debug, Clone)]
pub struct DebugEntry {
pub key: String,
pub value: String,
}
impl DebugEntry {
pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
Self {
key: key.into(),
value: value.into(),
}
}
}
#[derive(Debug, Clone)]
pub struct DebugSection {
pub title: String,
pub entries: Vec<DebugEntry>,
}
impl DebugSection {
pub fn new(title: impl Into<String>) -> Self {
Self {
title: title.into(),
entries: Vec::new(),
}
}
pub fn entry(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.entries.push(DebugEntry::new(key, value));
self
}
pub fn push_entry(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.entries.push(DebugEntry::new(key, value));
}
}
pub trait DebugState {
fn debug_sections(&self) -> Vec<DebugSection>;
fn build_debug_table(&self, title: impl Into<String>) -> DebugTableOverlay {
let mut builder = DebugTableBuilder::new();
for section in self.debug_sections() {
builder.push_section(§ion.title);
for entry in section.entries {
builder.push_entry(entry.key, entry.value);
}
}
builder.finish(title)
}
}
impl<T: std::fmt::Debug> DebugState for DebugWrapper<'_, T> {
fn debug_sections(&self) -> Vec<DebugSection> {
vec![DebugSection::new("Debug Output").entry("value", format!("{:#?}", self.0))]
}
}
pub struct DebugWrapper<'a, T>(pub &'a T);
impl DebugState for () {
fn debug_sections(&self) -> Vec<DebugSection> {
vec![]
}
}
impl<A: DebugState, B: DebugState> DebugState for (A, B) {
fn debug_sections(&self) -> Vec<DebugSection> {
let mut sections = self.0.debug_sections();
sections.extend(self.1.debug_sections());
sections
}
}
impl<A: DebugState, B: DebugState, C: DebugState> DebugState for (A, B, C) {
fn debug_sections(&self) -> Vec<DebugSection> {
let mut sections = self.0.debug_sections();
sections.extend(self.1.debug_sections());
sections.extend(self.2.debug_sections());
sections
}
}
impl<T: DebugState> DebugState for &T {
fn debug_sections(&self) -> Vec<DebugSection> {
(*self).debug_sections()
}
}
impl<T: DebugState> DebugState for &mut T {
fn debug_sections(&self) -> Vec<DebugSection> {
(**self).debug_sections()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestState {
name: String,
count: usize,
}
impl DebugState for TestState {
fn debug_sections(&self) -> Vec<DebugSection> {
vec![DebugSection::new("Test")
.entry("name", &self.name)
.entry("count", self.count.to_string())]
}
}
#[test]
fn test_debug_state_basic() {
let state = TestState {
name: "test".to_string(),
count: 42,
};
let sections = state.debug_sections();
assert_eq!(sections.len(), 1);
assert_eq!(sections[0].title, "Test");
assert_eq!(sections[0].entries.len(), 2);
assert_eq!(sections[0].entries[0].key, "name");
assert_eq!(sections[0].entries[0].value, "test");
}
#[test]
fn test_build_debug_table() {
let state = TestState {
name: "foo".to_string(),
count: 10,
};
let table = state.build_debug_table("State Info");
assert_eq!(table.title, "State Info");
assert_eq!(table.rows.len(), 3); }
#[test]
fn test_tuple_debug_state() {
struct StateA;
struct StateB;
impl DebugState for StateA {
fn debug_sections(&self) -> Vec<DebugSection> {
vec![DebugSection::new("A").entry("from", "A")]
}
}
impl DebugState for StateB {
fn debug_sections(&self) -> Vec<DebugSection> {
vec![DebugSection::new("B").entry("from", "B")]
}
}
let combined = (StateA, StateB);
let sections = combined.debug_sections();
assert_eq!(sections.len(), 2);
assert_eq!(sections[0].title, "A");
assert_eq!(sections[1].title, "B");
}
#[test]
fn test_debug_wrapper() {
#[derive(Debug)]
#[allow(dead_code)]
struct PlainStruct {
x: i32,
}
let s = PlainStruct { x: 42 };
let sections = DebugWrapper(&s).debug_sections();
assert_eq!(sections.len(), 1);
assert!(sections[0].entries[0].value.contains("42"));
}
}