use std::collections::HashMap;
use crate::core::key::Key;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TrieMatch {
None,
Partial,
Exact(String),
Ambiguous(String),
}
#[derive(Debug, Default)]
struct TrieNode {
children: HashMap<Key, TrieNode>,
command: Option<String>,
}
#[derive(Debug, Default)]
pub struct BindingTrie {
root: TrieNode,
}
impl BindingTrie {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, keys: &[Key], command: String) {
let mut node = &mut self.root;
for key in keys {
node = node.children.entry(key.clone()).or_default();
}
node.command = Some(command);
}
pub fn lookup(&self, keys: &[Key]) -> TrieMatch {
let mut node = &self.root;
for key in keys {
match node.children.get(key) {
Some(child) => node = child,
None => return TrieMatch::None,
}
}
match (&node.command, node.children.is_empty()) {
(Some(cmd), true) => TrieMatch::Exact(cmd.clone()),
(Some(cmd), false) => TrieMatch::Ambiguous(cmd.clone()),
(None, _) => TrieMatch::Partial,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::key::Key;
#[test]
fn single_key_exact() {
let mut t = BindingTrie::new();
t.insert(&[Key::plain("j")], "scroll down".into());
assert_eq!(
t.lookup(&[Key::plain("j")]),
TrieMatch::Exact("scroll down".into())
);
}
#[test]
fn multi_key_partial_then_exact() {
let mut t = BindingTrie::new();
t.insert(&[Key::plain("g"), Key::plain("g")], "scroll-to-perc 0".into());
assert_eq!(t.lookup(&[Key::plain("g")]), TrieMatch::Partial);
assert_eq!(
t.lookup(&[Key::plain("g"), Key::plain("g")]),
TrieMatch::Exact("scroll-to-perc 0".into())
);
}
#[test]
fn unknown_is_none() {
let mut t = BindingTrie::new();
t.insert(&[Key::plain("j")], "scroll down".into());
assert_eq!(t.lookup(&[Key::plain("x")]), TrieMatch::None);
}
#[test]
fn modifiers_distinguish_bindings() {
let mut t = BindingTrie::new();
t.insert(&[Key::plain("f")], "hint".into());
t.insert(
&[Key {
sym: "f".into(),
ctrl: true,
alt: false,
shift: false,
}],
"scroll-page down".into(),
);
assert_eq!(t.lookup(&[Key::plain("f")]), TrieMatch::Exact("hint".into()));
assert_eq!(
t.lookup(&[Key {
sym: "f".into(),
ctrl: true,
alt: false,
shift: false
}]),
TrieMatch::Exact("scroll-page down".into())
);
}
}