tui_dispatch_core/debug/
config.rs1use crate::keybindings::{BindingContext, Keybindings};
4use ratatui::style::{Color, Modifier, Style};
5
6#[derive(Debug, Clone)]
8pub struct DebugStyle {
9 pub banner_bg: Style,
11 pub title_style: Style,
13 pub key_style: Style,
15 pub label_style: Style,
17 pub value_style: Style,
19 pub dim_factor: f32,
21}
22
23impl Default for DebugStyle {
24 fn default() -> Self {
25 Self {
26 banner_bg: Style::default().bg(Color::Rgb(20, 20, 30)),
27 title_style: Style::default()
28 .fg(Color::Magenta)
29 .add_modifier(Modifier::BOLD),
30 key_style: Style::default().fg(Color::Rgb(20, 20, 30)).bg(Color::Cyan),
31 label_style: Style::default().fg(Color::Rgb(150, 150, 160)),
32 value_style: Style::default().fg(Color::White),
33 dim_factor: 0.7,
34 }
35 }
36}
37
38#[derive(Debug, Clone)]
40pub struct StatusItem {
41 pub label: String,
43 pub value: String,
45 pub style: Option<Style>,
47}
48
49impl StatusItem {
50 pub fn new(label: impl Into<String>, value: impl Into<String>) -> Self {
52 Self {
53 label: label.into(),
54 value: value.into(),
55 style: None,
56 }
57 }
58
59 pub fn with_style(mut self, style: Style) -> Self {
61 self.style = Some(style);
62 self
63 }
64}
65
66#[derive(Clone)]
68pub struct DebugConfig<C: BindingContext> {
69 pub keybindings: Keybindings<C>,
71 pub debug_context: C,
73 pub style: DebugStyle,
75 status_provider: Option<StatusProvider>,
77}
78
79type StatusProvider = std::sync::Arc<dyn Fn() -> Vec<StatusItem> + Send + Sync>;
80
81impl<C: BindingContext> std::fmt::Debug for DebugConfig<C> {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 f.debug_struct("DebugConfig")
84 .field("debug_context", &self.debug_context.name())
85 .field("style", &self.style)
86 .field(
87 "status_provider",
88 &self.status_provider.as_ref().map(|_| "<fn>"),
89 )
90 .finish()
91 }
92}
93
94impl<C: BindingContext> DebugConfig<C> {
95 pub fn new(keybindings: Keybindings<C>, debug_context: C) -> Self {
97 Self {
98 keybindings,
99 debug_context,
100 style: DebugStyle::default(),
101 status_provider: None,
102 }
103 }
104
105 pub fn with_style(mut self, style: DebugStyle) -> Self {
107 self.style = style;
108 self
109 }
110
111 pub fn with_status_provider<F>(mut self, provider: F) -> Self
115 where
116 F: Fn() -> Vec<StatusItem> + Send + Sync + 'static,
117 {
118 self.status_provider = Some(std::sync::Arc::new(provider));
119 self
120 }
121
122 pub fn status_items(&self) -> Vec<StatusItem> {
124 self.status_provider
125 .as_ref()
126 .map(|f| f())
127 .unwrap_or_default()
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
137 enum TestContext {
138 Debug,
139 }
140
141 impl BindingContext for TestContext {
142 fn name(&self) -> &'static str {
143 "debug"
144 }
145 fn from_name(name: &str) -> Option<Self> {
146 (name == "debug").then_some(TestContext::Debug)
147 }
148 fn all() -> &'static [Self] {
149 &[TestContext::Debug]
150 }
151 }
152
153 #[test]
154 fn test_status_item() {
155 let item = StatusItem::new("keys", "42");
156 assert_eq!(item.label, "keys");
157 assert_eq!(item.value, "42");
158 assert!(item.style.is_none());
159
160 let styled = item.with_style(Style::default().fg(Color::Red));
161 assert!(styled.style.is_some());
162 }
163
164 #[test]
165 fn test_config_with_status_provider() {
166 let config = DebugConfig::new(Keybindings::new(), TestContext::Debug)
167 .with_status_provider(|| vec![StatusItem::new("test", "value")]);
168
169 let items = config.status_items();
170 assert_eq!(items.len(), 1);
171 assert_eq!(items[0].label, "test");
172 }
173
174 #[test]
175 fn test_config_without_provider() {
176 let config: DebugConfig<TestContext> =
177 DebugConfig::new(Keybindings::new(), TestContext::Debug);
178 let items = config.status_items();
179 assert!(items.is_empty());
180 }
181}