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)? }) }