input_handler 0.1.0

Reusable input handler module
Documentation
use libc::STDIN_FILENO;
use std::io::{self, Read, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use termios::{tcsetattr, Termios, ECHO, ICANON, TCSANOW};

pub struct RawTerminal {
    original_termios: Termios,
}

impl RawTerminal {
    pub fn new() -> io::Result<Self> {
        let original_termios = Termios::from_fd(STDIN_FILENO)?;
        Ok(RawTerminal { original_termios })
    }

    pub fn raw_mode(&self) -> io::Result<()> {
        let mut raw = self.original_termios;
        raw.c_lflag &= !(ICANON | ECHO);
        tcsetattr(STDIN_FILENO, TCSANOW, &raw)?;
        Ok(())
    }
}

impl Drop for RawTerminal {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSANOW, &self.original_termios).ok();
    }
}

#[derive(Debug, PartialEq)]
pub enum KeyEvent {
    Char(char),
    CtrlC,
    CtrlD,
    Backspace,
    Enter,
    ArrowUp,
    ArrowDown,
    ArrowLeft,
    ArrowRight,
    Unknown,
}

pub struct TerminalReader {
    stdin: io::Stdin,
    running: Arc<AtomicBool>,
}

impl TerminalReader {
    pub fn new() -> Self {
        TerminalReader {
            stdin: io::stdin(),
            running: Arc::new(AtomicBool::new(true)),
        }
    }

    pub fn read_key(&mut self) -> io::Result<KeyEvent> {
        let mut buffer = [0; 4];
        match self.stdin.read(&mut buffer[..1])? {
            0 => Ok(KeyEvent::CtrlD),
            1 => match buffer[0] {
                3 => Ok(KeyEvent::CtrlC),
                13 | 10 => Ok(KeyEvent::Enter),
                127 => Ok(KeyEvent::Backspace),
                27 => {
                    if self.stdin.read(&mut buffer[1..3])? == 2 && buffer[1] == b'[' {
                        match buffer[2] {
                            b'A' => Ok(KeyEvent::ArrowUp),
                            b'B' => Ok(KeyEvent::ArrowDown),
                            b'C' => Ok(KeyEvent::ArrowRight),
                            b'D' => Ok(KeyEvent::ArrowLeft),
                            _ => Ok(KeyEvent::Unknown),
                        }
                    } else {
                        Ok(KeyEvent::Unknown)
                    }
                }
                c if c.is_ascii() => Ok(KeyEvent::Char(c as char)),
                _ => Ok(KeyEvent::Unknown),
            },
            _ => Ok(KeyEvent::Unknown),
        }
    }

    #[allow(dead_code)]
    pub fn stop(&self) {
        self.running.store(false, Ordering::SeqCst);
    }
}

pub fn clear_line() {
    print!("\r\x1b[K");
    io::stdout().flush().ok();
}

#[allow(dead_code)]
pub fn move_cursor_to_start() {
    print!("\r");
    io::stdout().flush().ok();
}