From 591d2b6167af53ce07b060711a4074f1e19c5f3f Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 8 May 2021 21:25:47 +0530 Subject: add basic user-definable keybinds --- src/keybind.rs | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/keybind.rs (limited to 'src/keybind.rs') diff --git a/src/keybind.rs b/src/keybind.rs new file mode 100644 index 0000000..8ab479a --- /dev/null +++ b/src/keybind.rs @@ -0,0 +1,146 @@ +use std::{fmt, str::FromStr}; + +use sdl2::keyboard::{Keycode, Mod}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Keybind { + keycode: Keycode, + keymod: Mod, +} + +impl Keybind { + pub fn new(keycode: Keycode, keymod: Mod) -> Self { + Self { keycode, keymod } + } +} + +// ctrl keys: C- len 3 +// shift keys: S- len 3 +// normal keys: len 1 + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum KeybindError { + Parse, + InvalidMod, + InvalidKey, +} + +impl fmt::Display for KeybindError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Parse => write!(f, "error parsing keybind"), + Self::InvalidKey => write!(f, "unsupported keybind, try `A-Z0-9`"), + Self::InvalidMod => { + write!(f, "unsupported keybind modifier, try `C- or S-`") + } + } + } +} + +impl std::error::Error for KeybindError {} + +impl FromStr for Keybind { + type Err = KeybindError; + fn from_str(s: &str) -> Result { + parse_from_str(s) + } +} + +fn parse_from_str(s: &str) -> Result { + let s = s.to_lowercase(); + match s.len() { + // either ctrl or shift key + 3 => { + let keymod = parse_modifier(&s[..2])?; + let keybind = parse_keybind(&s[2..])?; + Ok(Keybind::new(keybind, keymod)) + } + 1 => { + let keybind = parse_keybind(&s)?; + Ok(Keybind::new(keybind, Mod::NOMOD)) + } + _ => Err(KeybindError::Parse), + } +} + +fn parse_modifier(s: &str) -> Result { + match s { + "c-" => Ok(Mod::LCTRLMOD), + "s-" => Ok(Mod::LSHIFTMOD), + _ => Err(KeybindError::InvalidMod), + } +} + +fn parse_keybind(s: &str) -> Result { + match s.to_uppercase().as_str() { + "0" => Ok(Keycode::Num0), + "1" => Ok(Keycode::Num1), + "2" => Ok(Keycode::Num2), + "3" => Ok(Keycode::Num3), + "4" => Ok(Keycode::Num4), + "5" => Ok(Keycode::Num5), + "6" => Ok(Keycode::Num6), + "7" => Ok(Keycode::Num7), + "8" => Ok(Keycode::Num8), + "9" => Ok(Keycode::Num9), + "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" + | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" => { + Ok(Keycode::from_name(s).unwrap()) + } + _ => Err(KeybindError::InvalidKey), + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn without_mod() { + assert_eq!( + Keybind::from_str("x").unwrap(), + Keybind::new(Keycode::X, Mod::NOMOD) + ); + assert_eq!( + Keybind::from_str("X").unwrap(), + Keybind::new(Keycode::X, Mod::NOMOD) + ); + } + #[test] + fn with_ctrl_mod() { + assert_eq!( + Keybind::from_str("c-x").unwrap(), + Keybind::new(Keycode::X, Mod::LCTRLMOD) + ); + assert_eq!( + Keybind::from_str("C-x").unwrap(), + Keybind::new(Keycode::X, Mod::LCTRLMOD) + ); + assert_eq!( + Keybind::from_str("c-X").unwrap(), + Keybind::new(Keycode::X, Mod::LCTRLMOD) + ); + assert_eq!( + Keybind::from_str("C-X").unwrap(), + Keybind::new(Keycode::X, Mod::LCTRLMOD) + ); + } + #[test] + fn with_shift_mod() { + assert_eq!( + Keybind::from_str("s-x").unwrap(), + Keybind::new(Keycode::X, Mod::LSHIFTMOD) + ); + assert_eq!( + Keybind::from_str("S-x").unwrap(), + Keybind::new(Keycode::X, Mod::LSHIFTMOD) + ); + assert_eq!( + Keybind::from_str("s-X").unwrap(), + Keybind::new(Keycode::X, Mod::LSHIFTMOD) + ); + assert_eq!( + Keybind::from_str("S-X").unwrap(), + Keybind::new(Keycode::X, Mod::LSHIFTMOD) + ); + } +} -- cgit v1.2.3