#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum InputState {
Normal,
EscapeStart,
EscapeSequence,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum InputEvent {
None,
Char(char),
Backspace,
Enter,
Tab,
UpArrow,
DownArrow,
DoubleEsc,
}
#[derive(Debug)]
pub struct InputDecoder {
state: InputState,
}
impl InputDecoder {
pub fn new() -> Self {
Self {
state: InputState::Normal,
}
}
pub fn decode_char(&mut self, c: char) -> InputEvent {
match self.state {
InputState::Normal => self.decode_normal(c),
InputState::EscapeStart => self.decode_escape_start(c),
InputState::EscapeSequence => self.decode_escape_sequence(c),
}
}
fn decode_normal(&mut self, c: char) -> InputEvent {
match c {
'\x1b' => {
self.state = InputState::EscapeStart;
InputEvent::None
}
'\n' | '\r' => InputEvent::Enter,
'\t' => InputEvent::Tab,
'\x08' | '\x7f' => InputEvent::Backspace,
c if c.is_control() => InputEvent::None,
_ => InputEvent::Char(c),
}
}
fn decode_escape_start(&mut self, c: char) -> InputEvent {
match c {
'\x1b' => {
self.state = InputState::Normal;
InputEvent::DoubleEsc
}
'[' => {
self.state = InputState::EscapeSequence;
InputEvent::None
}
_ => {
self.state = InputState::Normal;
InputEvent::Char(c)
}
}
}
fn decode_escape_sequence(&mut self, c: char) -> InputEvent {
self.state = InputState::Normal;
match c {
'A' => InputEvent::UpArrow,
'B' => InputEvent::DownArrow,
_ => InputEvent::None,
}
}
pub fn reset(&mut self) {
self.state = InputState::Normal;
}
#[cfg(test)]
pub fn state(&self) -> InputState {
self.state
}
}
impl Default for InputDecoder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decoder_new() {
let decoder = InputDecoder::new();
assert_eq!(decoder.state(), InputState::Normal);
}
#[test]
fn test_decoder_default() {
let decoder = InputDecoder::default();
assert_eq!(decoder.state(), InputState::Normal);
}
#[test]
fn test_decoder_reset() {
let mut decoder = InputDecoder::new();
decoder.state = InputState::EscapeStart;
decoder.reset();
assert_eq!(decoder.state(), InputState::Normal);
}
#[test]
fn test_regular_characters() {
let mut decoder = InputDecoder::new();
let event = decoder.decode_char('h');
assert_eq!(event, InputEvent::Char('h'));
let event = decoder.decode_char('i');
assert_eq!(event, InputEvent::Char('i'));
}
#[test]
fn test_unicode_characters() {
let mut decoder = InputDecoder::new();
let event = decoder.decode_char('ø');
assert_eq!(event, InputEvent::Char('ø'));
let event = decoder.decode_char('£');
assert_eq!(event, InputEvent::Char('£'));
}
#[test]
fn test_spaces() {
let mut decoder = InputDecoder::new();
let event = decoder.decode_char(' ');
assert_eq!(event, InputEvent::Char(' '));
}
#[test]
fn test_enter_linefeed() {
let mut decoder = InputDecoder::new();
let event = decoder.decode_char('\n');
assert_eq!(event, InputEvent::Enter);
}
#[test]
fn test_enter_carriage_return() {
let mut decoder = InputDecoder::new();
let event = decoder.decode_char('\r');
assert_eq!(event, InputEvent::Enter);
}
#[test]
fn test_tab() {
let mut decoder = InputDecoder::new();
let event = decoder.decode_char('\t');
assert_eq!(event, InputEvent::Tab);
}
#[test]
fn test_backspace_ascii_bs() {
let mut decoder = InputDecoder::new();
let event = decoder.decode_char('\x08');
assert_eq!(event, InputEvent::Backspace);
}
#[test]
fn test_backspace_del() {
let mut decoder = InputDecoder::new();
let event = decoder.decode_char('\x7f');
assert_eq!(event, InputEvent::Backspace);
}
#[test]
fn test_single_esc_no_sequence() {
let mut decoder = InputDecoder::new();
let event = decoder.decode_char('\x1b');
assert_eq!(event, InputEvent::None);
assert_eq!(decoder.state(), InputState::EscapeStart);
}
#[test]
fn test_double_esc() {
let mut decoder = InputDecoder::new();
let event = decoder.decode_char('\x1b');
assert_eq!(event, InputEvent::None);
let event = decoder.decode_char('\x1b');
assert_eq!(event, InputEvent::DoubleEsc);
assert_eq!(decoder.state(), InputState::Normal);
}
#[test]
fn test_esc_bracket_starts_sequence() {
let mut decoder = InputDecoder::new();
decoder.decode_char('\x1b');
let event = decoder.decode_char('[');
assert_eq!(event, InputEvent::None);
assert_eq!(decoder.state(), InputState::EscapeSequence);
}
#[test]
fn test_up_arrow() {
let mut decoder = InputDecoder::new();
decoder.decode_char('\x1b');
decoder.decode_char('[');
let event = decoder.decode_char('A');
assert_eq!(event, InputEvent::UpArrow);
assert_eq!(decoder.state(), InputState::Normal);
}
#[test]
fn test_down_arrow() {
let mut decoder = InputDecoder::new();
decoder.decode_char('\x1b');
decoder.decode_char('[');
let event = decoder.decode_char('B');
assert_eq!(event, InputEvent::DownArrow);
assert_eq!(decoder.state(), InputState::Normal);
}
#[test]
fn test_unknown_escape_sequence() {
let mut decoder = InputDecoder::new();
decoder.decode_char('\x1b');
decoder.decode_char('[');
let event = decoder.decode_char('X');
assert_eq!(event, InputEvent::None);
assert_eq!(decoder.state(), InputState::Normal);
}
#[test]
fn test_esc_followed_by_regular_char() {
let mut decoder = InputDecoder::new();
decoder.decode_char('\x1b');
let event = decoder.decode_char('a');
assert_eq!(event, InputEvent::Char('a'));
assert_eq!(decoder.state(), InputState::Normal);
}
#[test]
fn test_control_characters_ignored() {
let mut decoder = InputDecoder::new();
for c in [
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
] {
let event = decoder.decode_char(c);
assert_eq!(event, InputEvent::None);
}
}
#[test]
fn test_complex_input_sequence() {
let mut decoder = InputDecoder::new();
assert_eq!(decoder.decode_char('h'), InputEvent::Char('h'));
assert_eq!(decoder.decode_char('e'), InputEvent::Char('e'));
assert_eq!(decoder.decode_char('l'), InputEvent::Char('l'));
assert_eq!(decoder.decode_char('l'), InputEvent::Char('l'));
assert_eq!(decoder.decode_char('o'), InputEvent::Char('o'));
assert_eq!(decoder.decode_char('\x7f'), InputEvent::Backspace);
assert_eq!(decoder.decode_char(' '), InputEvent::Char(' '));
assert_eq!(decoder.decode_char('w'), InputEvent::Char('w'));
assert_eq!(decoder.decode_char('o'), InputEvent::Char('o'));
assert_eq!(decoder.decode_char('r'), InputEvent::Char('r'));
assert_eq!(decoder.decode_char('l'), InputEvent::Char('l'));
assert_eq!(decoder.decode_char('d'), InputEvent::Char('d'));
}
#[test]
fn test_double_esc_then_type() {
let mut decoder = InputDecoder::new();
decoder.decode_char('\x1b');
assert_eq!(decoder.decode_char('\x1b'), InputEvent::DoubleEsc);
assert_eq!(decoder.decode_char('n'), InputEvent::Char('n'));
assert_eq!(decoder.decode_char('e'), InputEvent::Char('e'));
assert_eq!(decoder.decode_char('w'), InputEvent::Char('w'));
}
#[test]
fn test_arrow_keys_sequence() {
let mut decoder = InputDecoder::new();
decoder.decode_char('\x1b');
decoder.decode_char('[');
assert_eq!(decoder.decode_char('A'), InputEvent::UpArrow);
decoder.decode_char('\x1b');
decoder.decode_char('[');
assert_eq!(decoder.decode_char('B'), InputEvent::DownArrow);
}
}