oo-ide 0.0.4

∞ is a terminal IDE focused on low distraction, high usability.
Documentation
use logos::Logos;

#[derive(Logos, Debug, PartialEq)]
pub(crate) enum Token {
    #[token("Ctrl")]
    Ctrl,
    #[token("Shift")]
    Shift,
    #[token("Alt")]
    Alt,
    #[regex(r"[A-Za-z0-9]", |lex| lex.slice().chars().next().unwrap())]
    Char(char),
    #[regex(r"F[1-9][0-2]?", |lex| parse_function_key(lex.slice()))]
    FKey(u8),
    #[token("ArrowUp")]
    ArrowUp,
    #[token("ArrowDown")]
    ArrowDown,
    #[token("ArrowLeft")]
    ArrowLeft,
    #[token("ArrowRight")]
    ArrowRight,
    #[token("Enter")]
    Enter,
    #[token("Escape")]
    Escape,
    #[token("Tab")]
    Tab,
    #[token("Backspace")]
    Backspace,
    #[token("Delete")]
    Delete,
    #[token("+")]
    Plus,
}

fn parse_function_key(s: &str) -> Option<u8> {
    s.trim_start_matches('F').parse::<u8>().ok()
}

#[cfg(test)]
mod tests {
    use logos::Logos as _;

    use super::{Token, parse_function_key};

    // --- Token tests ---
    #[test]
    fn test_ctrl() {
        assert_eq!(Token::lexer("Ctrl").next().unwrap().unwrap(), Token::Ctrl);
    }

    #[test]
    fn test_shift() {
        assert_eq!(
            Token::lexer("Shift").next().unwrap().unwrap(),
            Token::Shift
        );
    }

    #[test]
    fn test_alt() {
        assert_eq!(Token::lexer("Alt").next().unwrap().unwrap(), Token::Alt);
    }

    #[test]
    fn test_char_lower() {
        assert_eq!(Token::lexer("a").next().unwrap().unwrap(), Token::Char('a'));
    }

    #[test]
    fn test_char_upper() {
        assert_eq!(Token::lexer("A").next().unwrap().unwrap(), Token::Char('A'));
    }

    #[test]
    fn test_char_digit() {
        assert_eq!(Token::lexer("5").next().unwrap().unwrap(), Token::Char('5'));
    }

    #[test]
    fn test_fkey_1() {
        assert_eq!(Token::lexer("F1").next().unwrap().unwrap(), Token::FKey(1));
    }

    #[test]
    fn test_fkey_9() {
        assert_eq!(Token::lexer("F9").next().unwrap().unwrap(), Token::FKey(9));
    }

    #[test]
    fn test_fkey_10() {
        assert_eq!(Token::lexer("F10").next().unwrap().unwrap(), Token::FKey(10));
    }

    #[test]
    fn test_fkey_12() {
        assert_eq!(
            Token::lexer("F12").next().unwrap().unwrap(),
            Token::FKey(12)
        );
    }

    #[test]
    fn test_arrow_up() {
        assert_eq!(
            Token::lexer("ArrowUp").next().unwrap().unwrap(),
            Token::ArrowUp
        );
    }

    #[test]
    fn test_arrow_down() {
        assert_eq!(
            Token::lexer("ArrowDown").next().unwrap().unwrap(),
            Token::ArrowDown
        );
    }

    #[test]
    fn test_arrow_left() {
        assert_eq!(
            Token::lexer("ArrowLeft").next().unwrap().unwrap(),
            Token::ArrowLeft
        );
    }

    #[test]
    fn test_arrow_right() {
        assert_eq!(
            Token::lexer("ArrowRight").next().unwrap().unwrap(),
            Token::ArrowRight
        );
    }

    #[test]
    fn test_enter() {
        assert_eq!(Token::lexer("Enter").next().unwrap().unwrap(), Token::Enter);
    }

    #[test]
    fn test_escape() {
        assert_eq!(
            Token::lexer("Escape").next().unwrap().unwrap(),
            Token::Escape
        );
    }

    #[test]
    fn test_tab() {
        assert_eq!(Token::lexer("Tab").next().unwrap().unwrap(), Token::Tab);
    }

    #[test]
    fn test_backspace() {
        assert_eq!(
            Token::lexer("Backspace").next().unwrap().unwrap(),
            Token::Backspace
        );
    }

    #[test]
    fn test_delete() {
        assert_eq!(
            Token::lexer("Delete").next().unwrap().unwrap(),
            Token::Delete
        );
    }

    #[test]
    fn test_plus() {
        assert_eq!(Token::lexer("+").next().unwrap().unwrap(), Token::Plus);
    }

    // --- Multiple tokens ---
    #[test]
    fn test_ctrl_plus_a() {
        let mut lexer = Token::lexer("Ctrl+a");
        assert_eq!(lexer.next().unwrap().unwrap(), Token::Ctrl);
        assert_eq!(lexer.next().unwrap().unwrap(), Token::Plus);
        assert_eq!(lexer.next().unwrap().unwrap(), Token::Char('a'));
        assert!(lexer.next().is_none());
    }

    #[test]
    fn test_shift_alt_f10() {
        let mut lexer = Token::lexer("Shift+Alt+F10");
        assert_eq!(lexer.next().unwrap().unwrap(), Token::Shift);
        assert_eq!(lexer.next().unwrap().unwrap(), Token::Plus);
        assert_eq!(lexer.next().unwrap().unwrap(), Token::Alt);
        assert_eq!(lexer.next().unwrap().unwrap(), Token::Plus);
        assert_eq!(lexer.next().unwrap().unwrap(), Token::FKey(10));
    }

    // --- Function key parsing ---
    #[test]
    fn test_parse_function_key_f1() {
        assert_eq!(parse_function_key("F1"), Some(1));
    }

    #[test]
    fn test_parse_function_key_f12() {
        assert_eq!(parse_function_key("F12"), Some(12));
    }

    #[test]
    fn test_parse_function_key_invalid() {
        assert_eq!(parse_function_key("F256"), None); // u8 overflow
        assert_eq!(parse_function_key("Invalid"), None); // non-numeric
    }
}