1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
use std::str::{self, Utf8Error};
use std::ops::RangeInclusive;

#[derive(Debug, Copy, Clone, Eq, Ord, PartialOrd, PartialEq)]
pub enum InputKey<'a> {
    AsciiPrintable(char),
    AsciiControl(char),
    Enter,
    Tab,
    Delete,
    Backspace,
    Esc,
    Up,
    Down,
    Left,
    Right,
    EscSeq(char),
    EscCsi(&'a str),
    UnicodeControl(char),
    UnicodeChar(char),
}

const CSI_TERMINATOR: RangeInclusive<char> = '\x40'..='\x7E';

pub struct InputKeyIter<'a> {
    buf: &'a str,
}

impl<'a> InputKeyIter<'a> {
    fn take_chars(&mut self, n: usize) -> &str {
        let idx = match self.buf.char_indices().nth(n) {
            Some((idx, _)) => idx,
            None => return self.buf,
        };
        let (head, tail) = self.buf.split_at(idx);
        self.buf = tail;
        head
    }

    fn take_char(&mut self) -> char {
        let mut chars = self.buf.chars();
        let ch = chars.next().expect("take_char: no next character");
        self.buf = chars.as_str();
        ch
    }

    fn peek_char(&self) -> Option<char> {
        self.buf.chars().next()
    }

    pub fn to_slice(&self) -> &str {
        self.buf
    }

    fn parse_csi(&mut self) -> InputKey<'a> {
        let (head, tail) = match self.buf.find(|ch| CSI_TERMINATOR.contains(&ch)) {
            Some(idx) => self.buf.split_at(idx+1),
            None => return InputKey::Esc,
        };
        self.buf = tail;
        match head {
            "A" => InputKey::Up,
            "B" => InputKey::Down,
            "C" => InputKey::Right,
            "D" => InputKey::Left,
            csi => InputKey::EscCsi(csi),
        }
    }
}

impl<'a> Iterator for InputKeyIter<'a> {
    type Item = InputKey<'a>;

    fn next(&mut self) -> Option<InputKey<'a>> {
        if self.buf.len() > 0 {
            let key = match self.take_char() as char {
                ch @ ' ' ... '~' => InputKey::AsciiPrintable(ch),
                '\n' => InputKey::Enter,
                '\r' => InputKey::Enter,
                '\t' => InputKey::Tab,
                '\x7f' => InputKey::Delete,
                '\x08' => InputKey::Backspace,
                '\x1b' if self.peek_char().is_some() => match self.take_char() {
                    '[' => self.parse_csi(),
                    ch => InputKey::EscSeq(ch),
                },
                ch @ '\x00' ... '\x1f' => InputKey::AsciiControl(std::char::from_u32((ch as u32) + 0x40).expect("Always succeeds.")),
                ch if ch.is_control() => InputKey::UnicodeControl(ch),
                ch => InputKey::UnicodeChar(ch),
            };
            Some(key)
        } else {
            None
        }
    }
}

pub fn parse_input(buf: &[u8]) -> Result<InputKeyIter<'_>, Utf8Error> {
    Ok(InputKeyIter {
        buf: str::from_utf8(buf)?
    })
}