use tokenizer::Mode;
use crate::{
error,
key_repr::{Key, KeyModifiers, KeyValue, MediaKeyCode},
};
#[cfg(feature = "modifier-keys")]
use crate::key_repr::ModifierKeyCode;
#[cfg(feature = "mouse-keys")]
use crate::key_repr::MouseKeyValue;
use self::tokenizer::{Token, Tokenizer};
pub(crate) mod tokenizer;
pub(crate) struct Parser<'a> {
ts: Tokenizer<'a>,
}
impl<'a> Parser<'a> {
pub(crate) fn new(input: &'a str) -> Self {
Self {
ts: Tokenizer::new(input),
}
}
pub(crate) fn is_empty(&self) -> bool {
self.ts.is_empty()
}
pub(crate) fn parse_full_key(&mut self, dash_index: usize) -> Result<Key, error::KeyParse> {
self.ts.expect(Token::AngularBracketOpen)?;
let modifiers = self.parse_modifiers(dash_index)?;
assert_eq!(self.ts.index(), dash_index);
self.ts.expect(Token::Dash)?;
let value = self.parse_value()?;
self.ts.expect(Token::AngularBracketClose)?;
Ok(Key { value, modifiers })
}
pub(crate) fn next_key(&mut self) -> Result<Key, error::KeyParse> {
let value;
let modifiers;
if self.ts.peek(Token::AngularBracketOpen)? {
if let Some(dash_index) = self.ts.find(Token::Dash)? {
if let Some(abc_index) = self.ts.find(Token::AngularBracketClose)? {
if abc_index < dash_index {
value = self.parse_special_value()?;
modifiers = KeyModifiers::default();
} else {
return self.parse_full_key(dash_index);
}
} else {
return self.parse_full_key(dash_index);
}
} else {
value = self.parse_special_value()?;
modifiers = KeyModifiers::default();
}
} else {
value = self.parse_normal_value()?;
modifiers = KeyModifiers::default();
}
Ok(Key { value, modifiers })
}
fn parse_modifiers(&mut self, dash_index: usize) -> Result<KeyModifiers, error::KeyParse> {
enum Mod {
Alt,
Ctrl,
Meta,
Shift,
}
let tokens: String = self
.ts
.take_while(|ch| ch != Token::Dash)?
.into_iter()
.map(Token::to_char)
.collect();
assert_eq!(self.ts.index(), dash_index);
let modifiers: Vec<Mod> = if tokens.contains('+') {
tokens
.split('+')
.map(|modifier| match modifier {
"Alt" => Ok(Mod::Alt),
"Ctrl" => Ok(Mod::Ctrl),
"Meta" | "Super" => Ok(Mod::Meta),
"Shift" => Ok(Mod::Shift),
other => Err(error::KeyParse::UnknownLongMod(other.to_owned())),
})
.collect::<Result<Vec<_>, error::KeyParse>>()
} else {
if tokens.contains(|ch: char| ch.is_lowercase()) {
match tokens.as_str() {
"Alt" => Ok(vec![Mod::Alt]),
"Ctrl" => Ok(vec![Mod::Ctrl]),
"Meta" | "Super" => Ok(vec![Mod::Meta]),
"Shift" => Ok(vec![Mod::Shift]),
other => Err(error::KeyParse::UnknownLongMod(other.to_owned())),
}
} else {
tokens
.chars()
.map(|ch| match ch {
'A' => Ok(Mod::Alt),
'C' => Ok(Mod::Ctrl),
'M' => Ok(Mod::Meta),
'S' => Ok(Mod::Shift),
other => Err(error::KeyParse::UnknownShortMod(other)),
})
.collect()
}
}?;
let mut output = KeyModifiers::default();
macro_rules! set_mod {
($mod:ident) => {
if output.$mod {
return Err(error::KeyParse::ModifierAlreadySet(
stringify!($mod).to_owned(),
));
} else {
output.$mod = true;
}
};
}
for modi in modifiers {
match modi {
Mod::Alt => set_mod!(alt),
Mod::Ctrl => set_mod!(ctrl),
Mod::Meta => set_mod!(meta),
Mod::Shift => set_mod!(shift),
}
}
Ok(output)
}
fn parse_value(&mut self) -> Result<KeyValue, error::KeyParse> {
if self.ts.peek(Token::AngularBracketOpen)? {
self.parse_special_value()
} else {
self.parse_normal_value()
}
}
fn parse_special_value(&mut self) -> Result<KeyValue, error::KeyParse> {
self.ts.expect(Token::AngularBracketOpen)?;
let tokens: String = self
.ts
.take_while(|ch| ch != Token::AngularBracketClose)?
.into_iter()
.map(Token::to_char)
.collect();
let value = match tokens.as_str() {
"BACKSPACE" => KeyValue::Backspace,
"ENTER" => KeyValue::Enter,
"LEFT" => KeyValue::Left,
"RIGHT" => KeyValue::Right,
"UP" => KeyValue::Up,
"DOWN" => KeyValue::Down,
"HOME" => KeyValue::Home,
"END" => KeyValue::End,
"PAGEUP" => KeyValue::PageUp,
"PAGEDOWN" => KeyValue::PageDown,
"TAB" => KeyValue::Tab,
"BACKTAB" => KeyValue::BackTab,
"DELETE" => KeyValue::Delete,
"INSERT" => KeyValue::Insert,
"ESC" => KeyValue::Esc,
"CAPSLOCK" => KeyValue::CapsLock,
"SCROLLlOCK" => KeyValue::ScrollLock,
"NUMLOCK" => KeyValue::NumLock,
"PRINTSCREEN" => KeyValue::PrintScreen,
"PAUSE" => KeyValue::Pause,
"MENU" => KeyValue::Menu,
"KEYPADBEGIN" => KeyValue::KeypadBegin,
"DASH" => KeyValue::Char('-'),
"ABO" | "ANGULAR_BRACKET_OPEN" => KeyValue::Char('<'),
"ABC" | "ANGULAR_BRACKET_CLOSE" => KeyValue::Char('>'),
f_char if f_char.starts_with('F') => {
let digits: u8 = f_char
.strip_prefix("F")
.expect("This starts with F, we checked")
.parse()
.map_err(error::KeyParse::CantParseFNumber)?;
KeyValue::F(digits)
}
#[cfg(feature = "mouse-keys")]
"MOUSE_RIGHT" => KeyValue::MouseKey(MouseKeyValue::Right),
#[cfg(feature = "mouse-keys")]
"MOUSE_LEFT" => KeyValue::MouseKey(MouseKeyValue::Left),
#[cfg(feature = "mouse-keys")]
"MOUSE_MIDDLE" => KeyValue::MouseKey(MouseKeyValue::Middle),
#[cfg(feature = "modifier-keys")]
"LEFT_ALT" => KeyValue::ModifierKey(ModifierKeyCode::LeftAlt),
#[cfg(feature = "modifier-keys")]
"RIGHT_ALT" => KeyValue::ModifierKey(ModifierKeyCode::RightAlt),
#[cfg(feature = "modifier-keys")]
"LEFT_CTRL" => KeyValue::ModifierKey(ModifierKeyCode::LeftCtrl),
#[cfg(feature = "modifier-keys")]
"RIGHT_CTRL" => KeyValue::ModifierKey(ModifierKeyCode::RightCtrl),
#[cfg(feature = "modifier-keys")]
"LEFT_META" | "LEFT_SUPER" => KeyValue::ModifierKey(ModifierKeyCode::LeftMeta),
#[cfg(feature = "modifier-keys")]
"RIGHT_META" | "RIGHT_SUPER" => KeyValue::ModifierKey(ModifierKeyCode::RightMeta),
#[cfg(feature = "modifier-keys")]
"LEFT_SHIFT" => KeyValue::ModifierKey(ModifierKeyCode::LeftShift),
#[cfg(feature = "modifier-keys")]
"RIGHT_SHIFT" => KeyValue::ModifierKey(ModifierKeyCode::RightShift),
"MEDIA_PLAY" => KeyValue::Media(MediaKeyCode::Play),
"MEDIA_PAUSE" => KeyValue::Media(MediaKeyCode::Pause),
"MEDIA_PLAYPAUSE" => KeyValue::Media(MediaKeyCode::PlayPause),
"MEDIA_REVERSE" => KeyValue::Media(MediaKeyCode::Reverse),
"MEDIA_STOP" => KeyValue::Media(MediaKeyCode::Stop),
"MEDIA_FASTFORWARD" => KeyValue::Media(MediaKeyCode::FastForward),
"MEDIA_REWIND" => KeyValue::Media(MediaKeyCode::Rewind),
"MEDIA_TRACKNEXT" => KeyValue::Media(MediaKeyCode::TrackNext),
"MEDIA_TRACKPREVIOUS" => KeyValue::Media(MediaKeyCode::TrackPrevious),
"MEDIA_RECORD" => KeyValue::Media(MediaKeyCode::Record),
"MEDIA_LOWERVOLUME" => KeyValue::Media(MediaKeyCode::LowerVolume),
"MEDIA_RAISEVOLUME" => KeyValue::Media(MediaKeyCode::RaiseVolume),
"MEDIA_MUTEVOLUME" => KeyValue::Media(MediaKeyCode::MuteVolume),
other => return Err(error::KeyParse::UnexpectedSpecialKey(other.to_owned())),
};
self.ts.expect(Token::AngularBracketClose)?;
Ok(value)
}
fn parse_normal_value(&mut self) -> Result<KeyValue, error::KeyParse> {
self.ts.mode = Mode::NormalValue;
let output = KeyValue::Char(self.ts.expect_char()?);
self.ts.mode = Mode::Default;
Ok(output)
}
}