use std::io::{self, Read};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Ansi<'a> {
Char(char),
Control(u8),
Esc(u8),
Csi(&'a [u8]),
}
const ESC: u8 = b'[' ^ 0x40;
const DEL: u8 = b'?' ^ 0x40;
pub struct Reader<R: Read> {
input: R,
buffer: Vec<u8>,
}
impl<R: Read> Reader<R> {
pub fn new(input: R) -> Self {
Self {
input,
buffer: Vec::new(),
}
}
#[inline]
fn next_byte(&mut self) -> io::Result<u8> {
let p = self.buffer.len();
self.buffer.push(0);
self.input.read_exact(&mut self.buffer[p..=p])?;
Ok(self.buffer[p])
}
pub fn read_sequence(&mut self) -> io::Result<Ansi> {
self.buffer.clear();
Ok(match self.next_byte()? {
b @ 0x80.. => {
let size = match b {
0xf0.. => 4,
0xe0.. => 3,
0xc0.. => 2,
_ => 1, };
self.buffer.extend([0; 3]);
self.input.read_exact(&mut self.buffer[1..size])?;
let str = std::str::from_utf8(&self.buffer[0..size]).unwrap();
let char = str.chars().next().unwrap();
Ansi::Char(char)
}
ESC => match self.next_byte()? {
b'[' => {
while !matches!(self.next_byte()?, 0x40..=0x7e) {}
Ansi::Csi(&self.buffer[2..])
}
c => Ansi::Esc(c),
},
c @ (..=0x1f | DEL) => Ansi::Control(c ^ 0x40),
c => Ansi::Char(char::from(c)),
})
}
}