Skip to main content

nut_shell/shell/
decoder.rs

1//! Input decoder for terminal character sequences.
2//!
3//! State machine for ANSI escape sequences (arrow keys) and double-ESC clear.
4//! Pure decoder: converts raw chars to logical events, no buffer or I/O management.
5
6/// Decoder state for escape sequence handling.
7#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8pub enum InputState {
9    /// Normal input mode
10    Normal,
11
12    /// Saw first ESC character
13    EscapeStart,
14
15    /// Saw ESC [ (start of escape sequence)
16    EscapeSequence,
17}
18
19/// Logical input event from terminal.
20#[derive(Debug, Copy, Clone, PartialEq, Eq)]
21pub enum InputEvent {
22    /// No event (accumulating sequence)
23    None,
24
25    /// Regular character typed
26    Char(char),
27
28    /// Backspace key (ASCII BS or DEL)
29    Backspace,
30
31    /// Enter key (line feed or carriage return)
32    Enter,
33
34    /// Tab key
35    Tab,
36
37    /// Up arrow key (history previous)
38    UpArrow,
39
40    /// Down arrow key (history next)
41    DownArrow,
42
43    /// Double ESC pressed
44    DoubleEsc,
45}
46
47/// Terminal input decoder with escape sequence state machine.
48/// Converts raw terminal chars to logical input events without managing buffers or I/O.
49#[derive(Debug)]
50pub struct InputDecoder {
51    /// Current decoder state
52    state: InputState,
53}
54
55impl InputDecoder {
56    /// Create new decoder in Normal state.
57    pub fn new() -> Self {
58        Self {
59            state: InputState::Normal,
60        }
61    }
62
63    /// Decode single character into input event (returns `None` for incomplete sequences).
64    pub fn decode_char(&mut self, c: char) -> InputEvent {
65        match self.state {
66            InputState::Normal => self.decode_normal(c),
67            InputState::EscapeStart => self.decode_escape_start(c),
68            InputState::EscapeSequence => self.decode_escape_sequence(c),
69        }
70    }
71
72    /// Decode character in Normal state (handle ESC, Enter, Tab, Backspace, or regular char).
73    fn decode_normal(&mut self, c: char) -> InputEvent {
74        match c {
75            // ESC - start of escape sequence
76            '\x1b' => {
77                self.state = InputState::EscapeStart;
78                InputEvent::None
79            }
80
81            // Enter - line feed or carriage return
82            '\n' | '\r' => InputEvent::Enter,
83
84            // Tab
85            '\t' => InputEvent::Tab,
86
87            // Backspace - ASCII BS (0x08) or DEL (0x7F)
88            '\x08' | '\x7f' => InputEvent::Backspace,
89
90            // Control characters (except those handled above) - ignore
91            c if c.is_control() => InputEvent::None,
92
93            // Regular printable character
94            _ => InputEvent::Char(c),
95        }
96    }
97
98    /// Decode character after seeing ESC.
99    fn decode_escape_start(&mut self, c: char) -> InputEvent {
100        match c {
101            // Second ESC = double-ESC
102            '\x1b' => {
103                self.state = InputState::Normal;
104                InputEvent::DoubleEsc
105            }
106
107            // '[' - start of escape sequence (arrow keys, etc.)
108            '[' => {
109                self.state = InputState::EscapeSequence;
110                InputEvent::None
111            }
112
113            // Any other character after ESC - treat as regular character
114            // This handles ESC followed by non-sequence characters
115            _ => {
116                self.state = InputState::Normal;
117                InputEvent::Char(c)
118            }
119        }
120    }
121
122    /// Decode character in escape sequence (after ESC [).
123    fn decode_escape_sequence(&mut self, c: char) -> InputEvent {
124        // Return to normal state
125        self.state = InputState::Normal;
126
127        match c {
128            // Arrow keys
129            'A' => InputEvent::UpArrow,
130            'B' => InputEvent::DownArrow,
131
132            // Future: could add C (right arrow), D (left arrow), H (home), F (end)
133            // Currently, only up/down arrows are implemented
134            // See PHILOSOPHY.md "Recommended Additions"
135
136            // Unknown sequence - ignore
137            _ => InputEvent::None,
138        }
139    }
140
141    /// Reset decoder state to Normal.
142    ///
143    /// Useful after handling special events or errors.
144    pub fn reset(&mut self) {
145        self.state = InputState::Normal;
146    }
147
148    /// Get current decoder state (for testing/debugging).
149    #[cfg(test)]
150    pub fn state(&self) -> InputState {
151        self.state
152    }
153}
154
155impl Default for InputDecoder {
156    fn default() -> Self {
157        Self::new()
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    // ========================================
166    // Basic Decoder State Tests
167    // ========================================
168
169    #[test]
170    fn test_decoder_new() {
171        let decoder = InputDecoder::new();
172        assert_eq!(decoder.state(), InputState::Normal);
173    }
174
175    #[test]
176    fn test_decoder_default() {
177        let decoder = InputDecoder::default();
178        assert_eq!(decoder.state(), InputState::Normal);
179    }
180
181    #[test]
182    fn test_decoder_reset() {
183        let mut decoder = InputDecoder::new();
184        decoder.state = InputState::EscapeStart;
185        decoder.reset();
186        assert_eq!(decoder.state(), InputState::Normal);
187    }
188
189    // ========================================
190    // Regular Character Decoding
191    // ========================================
192
193    #[test]
194    fn test_regular_characters() {
195        let mut decoder = InputDecoder::new();
196
197        let event = decoder.decode_char('h');
198        assert_eq!(event, InputEvent::Char('h'));
199
200        let event = decoder.decode_char('i');
201        assert_eq!(event, InputEvent::Char('i'));
202    }
203
204    #[test]
205    fn test_unicode_characters() {
206        let mut decoder = InputDecoder::new();
207
208        let event = decoder.decode_char('ø');
209        assert_eq!(event, InputEvent::Char('ø'));
210
211        let event = decoder.decode_char('£');
212        assert_eq!(event, InputEvent::Char('£'));
213    }
214
215    #[test]
216    fn test_spaces() {
217        let mut decoder = InputDecoder::new();
218
219        let event = decoder.decode_char(' ');
220        assert_eq!(event, InputEvent::Char(' '));
221    }
222
223    // ========================================
224    // Special Key Tests
225    // ========================================
226
227    #[test]
228    fn test_enter_linefeed() {
229        let mut decoder = InputDecoder::new();
230
231        let event = decoder.decode_char('\n');
232        assert_eq!(event, InputEvent::Enter);
233    }
234
235    #[test]
236    fn test_enter_carriage_return() {
237        let mut decoder = InputDecoder::new();
238
239        let event = decoder.decode_char('\r');
240        assert_eq!(event, InputEvent::Enter);
241    }
242
243    #[test]
244    fn test_tab() {
245        let mut decoder = InputDecoder::new();
246
247        let event = decoder.decode_char('\t');
248        assert_eq!(event, InputEvent::Tab);
249    }
250
251    // ========================================
252    // Backspace Tests
253    // ========================================
254
255    #[test]
256    fn test_backspace_ascii_bs() {
257        let mut decoder = InputDecoder::new();
258
259        let event = decoder.decode_char('\x08');
260        assert_eq!(event, InputEvent::Backspace);
261    }
262
263    #[test]
264    fn test_backspace_del() {
265        let mut decoder = InputDecoder::new();
266
267        let event = decoder.decode_char('\x7f');
268        assert_eq!(event, InputEvent::Backspace);
269    }
270
271    // ========================================
272    // Escape Sequence Tests
273    // ========================================
274
275    #[test]
276    fn test_single_esc_no_sequence() {
277        let mut decoder = InputDecoder::new();
278
279        // ESC should transition to EscapeStart
280        let event = decoder.decode_char('\x1b');
281        assert_eq!(event, InputEvent::None);
282        assert_eq!(decoder.state(), InputState::EscapeStart);
283    }
284
285    #[test]
286    fn test_double_esc() {
287        let mut decoder = InputDecoder::new();
288
289        // First ESC
290        let event = decoder.decode_char('\x1b');
291        assert_eq!(event, InputEvent::None);
292
293        // Second ESC
294        let event = decoder.decode_char('\x1b');
295        assert_eq!(event, InputEvent::DoubleEsc);
296        assert_eq!(decoder.state(), InputState::Normal);
297    }
298
299    #[test]
300    fn test_esc_bracket_starts_sequence() {
301        let mut decoder = InputDecoder::new();
302
303        // ESC [
304        decoder.decode_char('\x1b');
305        let event = decoder.decode_char('[');
306
307        assert_eq!(event, InputEvent::None);
308        assert_eq!(decoder.state(), InputState::EscapeSequence);
309    }
310
311    #[test]
312    fn test_up_arrow() {
313        let mut decoder = InputDecoder::new();
314
315        // ESC [ A
316        decoder.decode_char('\x1b');
317        decoder.decode_char('[');
318        let event = decoder.decode_char('A');
319
320        assert_eq!(event, InputEvent::UpArrow);
321        assert_eq!(decoder.state(), InputState::Normal);
322    }
323
324    #[test]
325    fn test_down_arrow() {
326        let mut decoder = InputDecoder::new();
327
328        // ESC [ B
329        decoder.decode_char('\x1b');
330        decoder.decode_char('[');
331        let event = decoder.decode_char('B');
332
333        assert_eq!(event, InputEvent::DownArrow);
334        assert_eq!(decoder.state(), InputState::Normal);
335    }
336
337    #[test]
338    fn test_unknown_escape_sequence() {
339        let mut decoder = InputDecoder::new();
340
341        // ESC [ X (unknown)
342        decoder.decode_char('\x1b');
343        decoder.decode_char('[');
344        let event = decoder.decode_char('X');
345
346        assert_eq!(event, InputEvent::None);
347        assert_eq!(decoder.state(), InputState::Normal);
348    }
349
350    #[test]
351    fn test_esc_followed_by_regular_char() {
352        let mut decoder = InputDecoder::new();
353
354        // ESC followed by 'a' (not a sequence)
355        decoder.decode_char('\x1b');
356        let event = decoder.decode_char('a');
357
358        assert_eq!(event, InputEvent::Char('a'));
359        assert_eq!(decoder.state(), InputState::Normal);
360    }
361
362    // ========================================
363    // Control Character Tests
364    // ========================================
365
366    #[test]
367    fn test_control_characters_ignored() {
368        let mut decoder = InputDecoder::new();
369
370        // Various control characters (except handled ones)
371        for c in [
372            '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
373        ] {
374            let event = decoder.decode_char(c);
375            assert_eq!(event, InputEvent::None);
376        }
377    }
378
379    // ========================================
380    // Integration Tests
381    // ========================================
382
383    #[test]
384    fn test_complex_input_sequence() {
385        let mut decoder = InputDecoder::new();
386
387        // Type "hello"
388        assert_eq!(decoder.decode_char('h'), InputEvent::Char('h'));
389        assert_eq!(decoder.decode_char('e'), InputEvent::Char('e'));
390        assert_eq!(decoder.decode_char('l'), InputEvent::Char('l'));
391        assert_eq!(decoder.decode_char('l'), InputEvent::Char('l'));
392        assert_eq!(decoder.decode_char('o'), InputEvent::Char('o'));
393
394        // Backspace
395        assert_eq!(decoder.decode_char('\x7f'), InputEvent::Backspace);
396
397        // Add space and more text
398        assert_eq!(decoder.decode_char(' '), InputEvent::Char(' '));
399        assert_eq!(decoder.decode_char('w'), InputEvent::Char('w'));
400        assert_eq!(decoder.decode_char('o'), InputEvent::Char('o'));
401        assert_eq!(decoder.decode_char('r'), InputEvent::Char('r'));
402        assert_eq!(decoder.decode_char('l'), InputEvent::Char('l'));
403        assert_eq!(decoder.decode_char('d'), InputEvent::Char('d'));
404    }
405
406    #[test]
407    fn test_double_esc_then_type() {
408        let mut decoder = InputDecoder::new();
409
410        // Double ESC
411        decoder.decode_char('\x1b');
412        assert_eq!(decoder.decode_char('\x1b'), InputEvent::DoubleEsc);
413
414        // Can type again after clear
415        assert_eq!(decoder.decode_char('n'), InputEvent::Char('n'));
416        assert_eq!(decoder.decode_char('e'), InputEvent::Char('e'));
417        assert_eq!(decoder.decode_char('w'), InputEvent::Char('w'));
418    }
419
420    #[test]
421    fn test_arrow_keys_sequence() {
422        let mut decoder = InputDecoder::new();
423
424        // Up arrow
425        decoder.decode_char('\x1b');
426        decoder.decode_char('[');
427        assert_eq!(decoder.decode_char('A'), InputEvent::UpArrow);
428
429        // Down arrow
430        decoder.decode_char('\x1b');
431        decoder.decode_char('[');
432        assert_eq!(decoder.decode_char('B'), InputEvent::DownArrow);
433    }
434}