keybac 0.1.1

simple keyboard capturing lib
Documentation
use std::cmp::PartialEq;
use std::io::{self, Read};
use std::os::unix::io::AsRawFd;
use std::time::Duration;
use libc::{termios, tcgetattr, tcsetattr, TCSANOW, ECHO, ICANON, fcntl, F_SETFL, O_NONBLOCK};
use crate::Keys::{Esc, Space};

pub struct KeyPress;

#[derive(Debug, PartialEq)]
pub enum Keys {
    Char(char),
    Ctrl(char),


    Esc,
    Space,
    Unknown,
}

impl Keys {
    pub fn to_string(&self) -> String {
        match self {
            Keys::Char(c) => c.to_string(),
            Keys::Ctrl(c) => format!("Ctrl+{}", c),

            Keys::Esc => "Esc".to_string(),
            Keys::Space => "Space".to_string(),
            Keys::Unknown => "Unknown".to_string(),
        }
    }

    pub fn is_escape(&self) -> bool {
        matches!(self, Keys::Esc)
    }

    pub fn is_space(&self) -> bool {
        matches!(self, Keys::Space)
    }

    pub fn is_ctrl_char(&self, ch: char) -> bool {
        matches!(self, Keys::Ctrl(c) if *c == ch)
    }
    pub fn is_char(&self, ch: char) -> bool {
        matches!(self, Keys::Char(c) if *c == ch)
    }
}

impl KeyPress {
    fn set_raw_mode(fd: i32, enable: bool) {
        let mut termios: termios = unsafe { std::mem::zeroed() };

        unsafe { tcgetattr(fd, &mut termios) };

        if enable {
            termios.c_lflag &= !(ICANON | ECHO);
        } else {
            termios.c_lflag |= ICANON | ECHO;
        }

        unsafe { tcsetattr(fd, TCSANOW, &termios) };
    }

    pub fn init() -> KeyPress {
        let stdin = io::stdin();
        let fd = stdin.as_raw_fd();
        Self::set_raw_mode(fd, true);

        unsafe {
            let flags = fcntl(fd, libc::F_GETFL);
            fcntl(fd, F_SETFL, flags | O_NONBLOCK);
        }

        KeyPress
    }

    pub fn read_key(&self) -> Option<Keys> {
        let mut stdin = io::stdin();
        let mut buffer = [0; 1];

        match stdin.read(&mut buffer) {
            Ok(1) => match buffer[0] {
                27 => Some(Keys::Esc),
                32 => Some(Keys::Space),
                c if c.is_ascii() && c.is_ascii_control() => {
                    let ctrl_char = (c+ 64) as char;
                    Some(Keys::Ctrl(c as char))
                },
                c if c.is_ascii() => Some(Keys::Char(c as char)),
                _ => Some(Keys::Unknown),
            },
            Ok(_) | Err(_) => None,
        }
    }

    pub fn get_key(&self) -> Option<String> {
        self.read_key().map(|key| key.to_string())
    }
}

impl Drop for KeyPress {
    fn drop(&mut self) {
        let stdin = io::stdin();
        let fd = stdin.as_raw_fd();
        KeyPress::set_raw_mode(fd, false);
    }
}