Skip to main content

oxihuman_core/
debug_console.rs

1//! Debug console stub — stores and queries log messages with severity levels.
2
3/// Severity level for a console entry.
4#[allow(dead_code)]
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub enum ConsoleSeverity {
7    Debug,
8    Info,
9    Warn,
10    Error,
11}
12
13/// A single log entry in the debug console.
14#[allow(dead_code)]
15#[derive(Clone, Debug)]
16pub struct ConsoleEntry {
17    pub severity: ConsoleSeverity,
18    pub message: String,
19    pub index: usize,
20}
21
22/// Configuration for the debug console.
23#[allow(dead_code)]
24pub struct DebugConsoleConfig {
25    /// Maximum number of entries to retain.
26    pub max_entries: usize,
27}
28
29/// The debug console — stores log entries with severity.
30#[allow(dead_code)]
31pub struct DebugConsole {
32    pub entries: Vec<ConsoleEntry>,
33    pub config: DebugConsoleConfig,
34    next_index: usize,
35}
36
37/// Returns a default `DebugConsoleConfig`.
38#[allow(dead_code)]
39pub fn default_debug_console_config() -> DebugConsoleConfig {
40    DebugConsoleConfig { max_entries: 1000 }
41}
42
43/// Creates a new `DebugConsole` from a config.
44#[allow(dead_code)]
45pub fn new_debug_console(cfg: &DebugConsoleConfig) -> DebugConsole {
46    DebugConsole {
47        entries: Vec::new(),
48        config: DebugConsoleConfig {
49            max_entries: cfg.max_entries,
50        },
51        next_index: 0,
52    }
53}
54
55/// Appends a log entry with the given severity and message.
56#[allow(dead_code)]
57pub fn console_log(console: &mut DebugConsole, severity: ConsoleSeverity, message: &str) {
58    let idx = console.next_index;
59    console.next_index += 1;
60    console.entries.push(ConsoleEntry {
61        severity,
62        message: message.to_string(),
63        index: idx,
64    });
65    if console.entries.len() > console.config.max_entries {
66        let overflow = console.entries.len() - console.config.max_entries;
67        console.entries.drain(0..overflow);
68    }
69}
70
71/// Returns the total number of entries in the console.
72#[allow(dead_code)]
73pub fn console_entry_count(console: &DebugConsole) -> usize {
74    console.entries.len()
75}
76
77/// Returns all entries matching the given severity.
78#[allow(dead_code)]
79pub fn console_entries_by_severity(
80    console: &DebugConsole,
81    severity: ConsoleSeverity,
82) -> Vec<&ConsoleEntry> {
83    console
84        .entries
85        .iter()
86        .filter(|e| e.severity == severity)
87        .collect()
88}
89
90/// Clears all log entries from the console.
91#[allow(dead_code)]
92pub fn console_clear(console: &mut DebugConsole) {
93    console.entries.clear();
94}
95
96/// Returns the last entry in the console, or `None` if empty.
97#[allow(dead_code)]
98pub fn console_last_entry(console: &DebugConsole) -> Option<&ConsoleEntry> {
99    console.entries.last()
100}
101
102/// Returns the count of entries with `ConsoleSeverity::Error`.
103#[allow(dead_code)]
104pub fn console_error_count(console: &DebugConsole) -> usize {
105    console
106        .entries
107        .iter()
108        .filter(|e| e.severity == ConsoleSeverity::Error)
109        .count()
110}
111
112/// Formats all console entries as a single multi-line string.
113#[allow(dead_code)]
114pub fn console_to_string(console: &DebugConsole) -> String {
115    console
116        .entries
117        .iter()
118        .map(|e| format!("[{}] {}", severity_name(e.severity), e.message))
119        .collect::<Vec<_>>()
120        .join("\n")
121}
122
123/// Returns the human-readable name for a `ConsoleSeverity`.
124#[allow(dead_code)]
125pub fn severity_name(s: ConsoleSeverity) -> &'static str {
126    match s {
127        ConsoleSeverity::Debug => "DEBUG",
128        ConsoleSeverity::Info => "INFO",
129        ConsoleSeverity::Warn => "WARN",
130        ConsoleSeverity::Error => "ERROR",
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    fn make_console() -> DebugConsole {
139        let cfg = default_debug_console_config();
140        new_debug_console(&cfg)
141    }
142
143    #[test]
144    fn test_new_console_empty() {
145        let c = make_console();
146        assert_eq!(console_entry_count(&c), 0);
147        assert!(console_last_entry(&c).is_none());
148    }
149
150    #[test]
151    fn test_console_log_info() {
152        let mut c = make_console();
153        console_log(&mut c, ConsoleSeverity::Info, "hello");
154        assert_eq!(console_entry_count(&c), 1);
155        let last = console_last_entry(&c).expect("should succeed");
156        assert_eq!(last.message, "hello");
157        assert_eq!(last.severity, ConsoleSeverity::Info);
158    }
159
160    #[test]
161    fn test_console_entries_by_severity() {
162        let mut c = make_console();
163        console_log(&mut c, ConsoleSeverity::Info, "info msg");
164        console_log(&mut c, ConsoleSeverity::Error, "error msg");
165        console_log(&mut c, ConsoleSeverity::Warn, "warn msg");
166        let errors = console_entries_by_severity(&c, ConsoleSeverity::Error);
167        assert_eq!(errors.len(), 1);
168        assert_eq!(errors[0].message, "error msg");
169    }
170
171    #[test]
172    fn test_console_clear() {
173        let mut c = make_console();
174        console_log(&mut c, ConsoleSeverity::Debug, "d1");
175        console_log(&mut c, ConsoleSeverity::Debug, "d2");
176        console_clear(&mut c);
177        assert_eq!(console_entry_count(&c), 0);
178        assert!(console_last_entry(&c).is_none());
179    }
180
181    #[test]
182    fn test_console_error_count() {
183        let mut c = make_console();
184        console_log(&mut c, ConsoleSeverity::Info, "i1");
185        console_log(&mut c, ConsoleSeverity::Error, "e1");
186        console_log(&mut c, ConsoleSeverity::Error, "e2");
187        assert_eq!(console_error_count(&c), 2);
188    }
189
190    #[test]
191    fn test_console_to_string() {
192        let mut c = make_console();
193        console_log(&mut c, ConsoleSeverity::Info, "startup");
194        console_log(&mut c, ConsoleSeverity::Warn, "low memory");
195        let s = console_to_string(&c);
196        assert!(s.contains("[INFO] startup"));
197        assert!(s.contains("[WARN] low memory"));
198    }
199
200    #[test]
201    fn test_severity_name() {
202        assert_eq!(severity_name(ConsoleSeverity::Debug), "DEBUG");
203        assert_eq!(severity_name(ConsoleSeverity::Info), "INFO");
204        assert_eq!(severity_name(ConsoleSeverity::Warn), "WARN");
205        assert_eq!(severity_name(ConsoleSeverity::Error), "ERROR");
206    }
207
208    #[test]
209    fn test_max_entries_trimmed() {
210        let cfg = DebugConsoleConfig { max_entries: 3 };
211        let mut c = new_debug_console(&cfg);
212        for i in 0..5 {
213            console_log(&mut c, ConsoleSeverity::Info, &format!("msg {}", i));
214        }
215        assert_eq!(console_entry_count(&c), 3);
216        // Oldest entries dropped — last entry should be msg 4
217        assert_eq!(
218            console_last_entry(&c).expect("should succeed").message,
219            "msg 4"
220        );
221    }
222
223    #[test]
224    fn test_console_last_entry_multiple() {
225        let mut c = make_console();
226        console_log(&mut c, ConsoleSeverity::Info, "first");
227        console_log(&mut c, ConsoleSeverity::Error, "last");
228        let last = console_last_entry(&c).expect("should succeed");
229        assert_eq!(last.message, "last");
230        assert_eq!(last.severity, ConsoleSeverity::Error);
231    }
232
233    #[test]
234    fn test_entries_by_severity_empty_result() {
235        let mut c = make_console();
236        console_log(&mut c, ConsoleSeverity::Info, "info only");
237        let debug_entries = console_entries_by_severity(&c, ConsoleSeverity::Debug);
238        assert!(debug_entries.is_empty());
239    }
240}