use logos::Logos as _;
use crate::prelude::*;
use input::{Key, KeyEvent, Modifiers};
use shortcuts::lexer::Token;
pub fn parse_key_combo(input: &str) -> Result<KeyEvent> {
let mut modifiers = Modifiers::empty();
let mut key: Option<Key> = None;
let lexer = Token::lexer(input);
for token in lexer {
match token.map_err(|_| anyhow!("Failed to parse {:?}", input))? {
Token::Ctrl => modifiers.insert(Modifiers::CTRL),
Token::Shift => modifiers.insert(Modifiers::SHIFT),
Token::Alt => modifiers.insert(Modifiers::ALT),
Token::Char(c) => key = Some(Key::Char(c.to_ascii_lowercase())),
Token::FKey(n) => key = Some(Key::F(n)),
Token::ArrowUp => key = Some(Key::ArrowUp),
Token::ArrowDown => key = Some(Key::ArrowDown),
Token::ArrowLeft => key = Some(Key::ArrowLeft),
Token::ArrowRight => key = Some(Key::ArrowRight),
Token::Enter => key = Some(Key::Enter),
Token::Escape => key = Some(Key::Escape),
Token::Tab => key = Some(Key::Tab),
Token::Backspace => key = Some(Key::Backspace),
Token::Delete => key = Some(Key::Delete),
Token::Plus => {}
}
}
Ok(KeyEvent {
modifiers,
key: key.ok_or_else(|| anyhow!("Missing key in shortcut '{input}'."))?,
})
}
#[cfg(test)]
mod tests {
use crate::{
input::{Key, Modifiers},
shortcuts::parser::parse_key_combo,
};
#[test]
fn test_parse_single_char() {
let result = parse_key_combo("a").unwrap();
assert_eq!(result.key, Key::Char('a'));
assert!(result.modifiers.is_empty());
}
#[test]
fn test_parse_upper_char_becomes_lower() {
let result = parse_key_combo("A").unwrap();
assert_eq!(result.key, Key::Char('a'));
}
#[test]
fn test_parse_digit() {
let result = parse_key_combo("5").unwrap();
assert_eq!(result.key, Key::Char('5'));
}
#[test]
fn test_parse_fkey() {
let result = parse_key_combo("F12").unwrap();
assert_eq!(result.key, Key::F(12));
}
#[test]
fn test_parse_arrow_up() {
let result = parse_key_combo("ArrowUp").unwrap();
assert_eq!(result.key, Key::ArrowUp);
}
#[test]
fn test_parse_arrow_down() {
let result = parse_key_combo("ArrowDown").unwrap();
assert_eq!(result.key, Key::ArrowDown);
}
#[test]
fn test_parse_arrow_left() {
let result = parse_key_combo("ArrowLeft").unwrap();
assert_eq!(result.key, Key::ArrowLeft);
}
#[test]
fn test_parse_arrow_right() {
let result = parse_key_combo("ArrowRight").unwrap();
assert_eq!(result.key, Key::ArrowRight);
}
#[test]
fn test_parse_enter() {
let result = parse_key_combo("Enter").unwrap();
assert_eq!(result.key, Key::Enter);
}
#[test]
fn test_parse_escape() {
let result = parse_key_combo("Escape").unwrap();
assert_eq!(result.key, Key::Escape);
}
#[test]
fn test_parse_tab() {
let result = parse_key_combo("Tab").unwrap();
assert_eq!(result.key, Key::Tab);
}
#[test]
fn test_parse_backspace() {
let result = parse_key_combo("Backspace").unwrap();
assert_eq!(result.key, Key::Backspace);
}
#[test]
fn test_parse_delete() {
let result = parse_key_combo("Delete").unwrap();
assert_eq!(result.key, Key::Delete);
}
#[test]
fn test_parse_ctrl_a() {
let result = parse_key_combo("Ctrl+a").unwrap();
assert_eq!(result.key, Key::Char('a'));
assert!(result.modifiers.contains(Modifiers::CTRL));
assert!(!result.modifiers.contains(Modifiers::ALT));
assert!(!result.modifiers.contains(Modifiers::SHIFT));
}
#[test]
fn test_parse_shift_enter() {
let result = parse_key_combo("Shift+Enter").unwrap();
assert_eq!(result.key, Key::Enter);
assert!(result.modifiers.contains(Modifiers::SHIFT));
}
#[test]
fn test_parse_alt_f4() {
let result = parse_key_combo("Alt+F4").unwrap();
assert_eq!(result.key, Key::F(4));
assert!(result.modifiers.contains(Modifiers::ALT));
}
#[test]
fn test_parse_ctrl_shift_c() {
let result = parse_key_combo("Ctrl+Shift+c").unwrap();
assert_eq!(result.key, Key::Char('c'));
assert!(result.modifiers.contains(Modifiers::CTRL));
assert!(result.modifiers.contains(Modifiers::SHIFT));
assert!(!result.modifiers.contains(Modifiers::ALT));
}
#[test]
fn test_parse_ctrl_alt_delete() {
let result = parse_key_combo("Ctrl+Alt+Delete").unwrap();
assert_eq!(result.key, Key::Delete);
assert!(result.modifiers.contains(Modifiers::CTRL));
assert!(result.modifiers.contains(Modifiers::ALT));
assert!(!result.modifiers.contains(Modifiers::SHIFT));
}
#[test]
fn test_parse_all_modifiers() {
let result = parse_key_combo("Ctrl+Shift+Alt+x").unwrap();
assert_eq!(result.key, Key::Char('x'));
assert!(result.modifiers.contains(Modifiers::CTRL));
assert!(result.modifiers.contains(Modifiers::SHIFT));
assert!(result.modifiers.contains(Modifiers::ALT));
}
#[test]
fn test_parse_missing_key() {
let result = parse_key_combo("Ctrl+");
assert!(result.is_err());
}
#[test]
fn test_parse_only_modifiers() {
let result = parse_key_combo("Ctrl+Shift+");
assert!(result.is_err());
}
#[test]
fn test_parse_empty_string() {
let result = parse_key_combo("");
assert!(result.is_err());
}
#[test]
fn test_parse_invalid_sequence() {
let result = parse_key_combo("Ctrl+Alt+Shift+");
assert!(result.is_err());
}
}