#![cfg(target_os = "macos")]
#![allow(dead_code)]
pub(crate) const MOD_SHIFT: u64 = 1 << 17;
pub(crate) const MOD_CONTROL: u64 = 1 << 18;
pub(crate) const MOD_OPTION: u64 = 1 << 19;
pub(crate) const MOD_COMMAND: u64 = 1 << 20;
pub(crate) fn parse_shortcut(shortcut: Option<&str>) -> (String, u64) {
let Some(raw) = shortcut.map(|s| s.trim()).filter(|s| !s.is_empty()) else {
return (String::new(), 0);
};
let mut modifiers: u64 = 0;
let mut key = String::new();
for ch in raw.chars() {
match ch {
'⌘' => modifiers |= MOD_COMMAND,
'⌃' | '^' => modifiers |= MOD_CONTROL,
'⌥' => modifiers |= MOD_OPTION,
'⇧' => modifiers |= MOD_SHIFT,
_ => {}
}
}
for part in raw.split('+') {
let token = part.trim().to_lowercase();
let token = token.trim_start_matches(['⌘', '⌃', '^', '⌥', '⇧']).to_string();
match token.as_str() {
"primary" | "cmdorctrl" | "cmd" | "command" | "meta" => modifiers |= MOD_COMMAND,
"ctrl" | "control" => modifiers |= MOD_CONTROL,
"alt" | "option" => modifiers |= MOD_OPTION,
"shift" => modifiers |= MOD_SHIFT,
"enter" | "return" => key = "\r".to_string(),
"tab" => key = "\t".to_string(),
"space" => key = " ".to_string(),
"delete" | "del" | "backspace" => key = "\u{8}".to_string(),
"esc" | "escape" => key = "\u{1b}".to_string(),
"left" => key = "\u{f702}".to_string(),
"right" => key = "\u{f703}".to_string(),
"up" => key = "\u{f700}".to_string(),
"down" => key = "\u{f701}".to_string(),
"pageup" | "pgup" => key = "\u{f72c}".to_string(),
"pagedown" | "pgdn" => key = "\u{f72d}".to_string(),
"home" => key = "\u{f729}".to_string(),
"end" => key = "\u{f72b}".to_string(),
"⌘" | "⌃" | "⌥" | "⇧" | "↩" | "⇥" | "⎋" | "⌫" | "←" | "→" | "↑" | "↓" | "⇞" | "⇟"
| "↖" | "↘" => {
}
_ if !token.is_empty() => {
key = token;
}
_ => { }
}
}
if key.is_empty() {
let stripped: String =
raw.chars().filter(|ch| !matches!(ch, '⌘' | '⌃' | '^' | '⌥' | '⇧')).collect();
let stripped = stripped.trim().to_string();
if !stripped.is_empty() {
key = match stripped.as_str() {
"↩" => "\r".to_string(),
"⇥" => "\t".to_string(),
"⎋" => "\u{1b}".to_string(),
"⌫" => "\u{8}".to_string(),
"←" => "\u{f702}".to_string(),
"→" => "\u{f703}".to_string(),
"↑" => "\u{f700}".to_string(),
"↓" => "\u{f701}".to_string(),
"⇞" => "\u{f72c}".to_string(),
"⇟" => "\u{f72d}".to_string(),
"↖" => "\u{f729}".to_string(),
"↘" => "\u{f72b}".to_string(),
other => other.to_string(),
};
}
}
if !key.is_empty() && modifiers == 0 {
modifiers = MOD_COMMAND;
}
(key, modifiers)
}
#[cfg(test)]
mod parse_shortcut_tests {
use super::{parse_shortcut, MOD_COMMAND, MOD_CONTROL, MOD_OPTION, MOD_SHIFT};
#[test]
fn absent_shortcut_produces_no_key() {
assert_eq!(parse_shortcut(None), (String::new(), 0));
assert_eq!(parse_shortcut(Some("")), (String::new(), 0));
assert_eq!(parse_shortcut(Some(" ")), (String::new(), 0));
}
#[test]
fn primary_spelling_maps_to_command() {
let (key, mask) = parse_shortcut(Some("Primary+S"));
assert_eq!(key, "s");
assert_eq!(mask, MOD_COMMAND);
}
#[test]
fn glyph_form_maps_to_command() {
let (key, mask) = parse_shortcut(Some("⌘S"));
assert_eq!(key, "s");
assert_eq!(mask, MOD_COMMAND);
}
#[test]
fn control_and_shift_are_distinct_from_command() {
let (key, mask) = parse_shortcut(Some("Ctrl+Shift+Z"));
assert_eq!(key, "z");
assert_eq!(mask, MOD_CONTROL | MOD_SHIFT);
}
#[test]
fn glyph_combo_recovers_named_key() {
let (key, mask) = parse_shortcut(Some("⇧⌘Z"));
assert_eq!(key, "z");
assert_eq!(mask, MOD_SHIFT | MOD_COMMAND);
}
#[test]
fn option_spelling_maps_to_option() {
let (key, mask) = parse_shortcut(Some("Alt+F4"));
assert_eq!(key, "f4");
assert_eq!(mask, MOD_OPTION);
}
#[test]
fn bare_key_defaults_to_command() {
let (key, mask) = parse_shortcut(Some("F1"));
assert_eq!(key, "f1");
assert_eq!(mask, MOD_COMMAND);
}
}