tui_dispatch_core/debug/
state.rs1use super::table::{DebugTableBuilder, DebugTableOverlay};
6
7#[derive(Debug, Clone)]
9pub struct DebugEntry {
10 pub key: String,
11 pub value: String,
12}
13
14impl DebugEntry {
15 pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
17 Self {
18 key: key.into(),
19 value: value.into(),
20 }
21 }
22}
23
24#[derive(Debug, Clone)]
26pub struct DebugSection {
27 pub title: String,
28 pub entries: Vec<DebugEntry>,
29}
30
31impl DebugSection {
32 pub fn new(title: impl Into<String>) -> Self {
34 Self {
35 title: title.into(),
36 entries: Vec::new(),
37 }
38 }
39
40 pub fn entry(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
42 self.entries.push(DebugEntry::new(key, value));
43 self
44 }
45
46 pub fn push_entry(&mut self, key: impl Into<String>, value: impl Into<String>) {
48 self.entries.push(DebugEntry::new(key, value));
49 }
50}
51
52pub trait DebugState {
80 fn debug_sections(&self) -> Vec<DebugSection>;
82
83 fn build_debug_table(&self, title: impl Into<String>) -> DebugTableOverlay {
87 let mut builder = DebugTableBuilder::new();
88 for section in self.debug_sections() {
89 builder.push_section(§ion.title);
90 for entry in section.entries {
91 builder.push_entry(entry.key, entry.value);
92 }
93 }
94 builder.finish(title)
95 }
96}
97
98impl<T: std::fmt::Debug> DebugState for DebugWrapper<'_, T> {
103 fn debug_sections(&self) -> Vec<DebugSection> {
104 vec![DebugSection::new("Debug Output").entry("value", format!("{:#?}", self.0))]
105 }
106}
107
108pub struct DebugWrapper<'a, T>(pub &'a T);
122
123impl DebugState for () {
125 fn debug_sections(&self) -> Vec<DebugSection> {
126 vec![]
127 }
128}
129
130impl<A: DebugState, B: DebugState> DebugState for (A, B) {
132 fn debug_sections(&self) -> Vec<DebugSection> {
133 let mut sections = self.0.debug_sections();
134 sections.extend(self.1.debug_sections());
135 sections
136 }
137}
138
139impl<A: DebugState, B: DebugState, C: DebugState> DebugState for (A, B, C) {
140 fn debug_sections(&self) -> Vec<DebugSection> {
141 let mut sections = self.0.debug_sections();
142 sections.extend(self.1.debug_sections());
143 sections.extend(self.2.debug_sections());
144 sections
145 }
146}
147
148impl<T: DebugState> DebugState for &T {
150 fn debug_sections(&self) -> Vec<DebugSection> {
151 (*self).debug_sections()
152 }
153}
154
155impl<T: DebugState> DebugState for &mut T {
156 fn debug_sections(&self) -> Vec<DebugSection> {
157 (**self).debug_sections()
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 struct TestState {
166 name: String,
167 count: usize,
168 }
169
170 impl DebugState for TestState {
171 fn debug_sections(&self) -> Vec<DebugSection> {
172 vec![DebugSection::new("Test")
173 .entry("name", &self.name)
174 .entry("count", self.count.to_string())]
175 }
176 }
177
178 #[test]
179 fn test_debug_state_basic() {
180 let state = TestState {
181 name: "test".to_string(),
182 count: 42,
183 };
184
185 let sections = state.debug_sections();
186 assert_eq!(sections.len(), 1);
187 assert_eq!(sections[0].title, "Test");
188 assert_eq!(sections[0].entries.len(), 2);
189 assert_eq!(sections[0].entries[0].key, "name");
190 assert_eq!(sections[0].entries[0].value, "test");
191 }
192
193 #[test]
194 fn test_build_debug_table() {
195 let state = TestState {
196 name: "foo".to_string(),
197 count: 10,
198 };
199
200 let table = state.build_debug_table("State Info");
201 assert_eq!(table.title, "State Info");
202 assert_eq!(table.rows.len(), 3); }
204
205 #[test]
206 fn test_tuple_debug_state() {
207 struct StateA;
208 struct StateB;
209
210 impl DebugState for StateA {
211 fn debug_sections(&self) -> Vec<DebugSection> {
212 vec![DebugSection::new("A").entry("from", "A")]
213 }
214 }
215
216 impl DebugState for StateB {
217 fn debug_sections(&self) -> Vec<DebugSection> {
218 vec![DebugSection::new("B").entry("from", "B")]
219 }
220 }
221
222 let combined = (StateA, StateB);
223 let sections = combined.debug_sections();
224 assert_eq!(sections.len(), 2);
225 assert_eq!(sections[0].title, "A");
226 assert_eq!(sections[1].title, "B");
227 }
228
229 #[test]
230 fn test_debug_wrapper() {
231 #[derive(Debug)]
232 #[allow(dead_code)]
233 struct PlainStruct {
234 x: i32,
235 }
236
237 let s = PlainStruct { x: 42 };
238 let sections = DebugWrapper(&s).debug_sections();
239 assert_eq!(sections.len(), 1);
240 assert!(sections[0].entries[0].value.contains("42"));
241 }
242}