use std::collections::HashMap;
use crate::input::KeyInput;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Keymap<A> {
bindings: HashMap<KeyInput, A>,
}
impl<A> Default for Keymap<A> {
fn default() -> Self {
Keymap {
bindings: HashMap::new(),
}
}
}
impl<A> Keymap<A> {
#[must_use]
pub fn new() -> Self {
Keymap::default()
}
pub fn bind(&mut self, input: KeyInput, action: A) -> Option<A> {
self.bindings.insert(input, action)
}
pub fn unbind(&mut self, input: &KeyInput) -> Option<A> {
self.bindings.remove(input)
}
#[must_use]
pub fn get(&self, input: &KeyInput) -> Option<&A> {
self.bindings.get(input)
}
#[must_use]
pub fn contains(&self, input: &KeyInput) -> bool {
self.bindings.contains_key(input)
}
#[must_use]
pub fn len(&self) -> usize {
self.bindings.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.bindings.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&KeyInput, &A)> {
self.bindings.iter()
}
}
pub fn resolve_layered<'a, A, L>(layers: L, input: &KeyInput) -> Option<&'a A>
where
L: IntoIterator<Item = &'a Keymap<A>>,
A: 'a,
{
layers.into_iter().find_map(|layer| layer.get(input))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::input::{Key, Modifiers};
#[derive(Debug, Clone, PartialEq)]
enum Action {
Quit,
Save,
Split,
}
fn ctrl(c: char) -> KeyInput {
KeyInput::new(Key::Char(c), Modifiers::CTRL)
}
#[test]
fn bind_then_get() {
let mut map = Keymap::new();
assert!(map.is_empty());
assert_eq!(map.bind(ctrl('q'), Action::Quit), None);
assert_eq!(map.get(&ctrl('q')), Some(&Action::Quit));
assert_eq!(map.len(), 1);
}
#[test]
fn miss_is_none_not_error() {
let map: Keymap<Action> = Keymap::new();
assert_eq!(map.get(&ctrl('q')), None);
assert!(!map.contains(&ctrl('q')));
}
#[test]
fn rebinding_returns_previous_action() {
let mut map = Keymap::new();
map.bind(ctrl('s'), Action::Save);
let previous = map.bind(ctrl('s'), Action::Quit);
assert_eq!(previous, Some(Action::Save));
assert_eq!(map.get(&ctrl('s')), Some(&Action::Quit));
}
#[test]
fn unbind_removes() {
let mut map = Keymap::new();
map.bind(ctrl('q'), Action::Quit);
assert_eq!(map.unbind(&ctrl('q')), Some(Action::Quit));
assert_eq!(map.get(&ctrl('q')), None);
}
#[test]
fn modifiers_are_part_of_the_key() {
let mut map = Keymap::new();
map.bind(ctrl('q'), Action::Quit);
let plain = KeyInput::new(Key::Char('q'), Modifiers::NONE);
assert_eq!(map.get(&plain), None);
}
#[test]
fn layered_resolution_prefers_earlier_layers_and_falls_through() {
let mut base = Keymap::new();
base.bind(ctrl('s'), Action::Save);
base.bind(ctrl('q'), Action::Quit);
let mut overlay = Keymap::new();
overlay.bind(ctrl('s'), Action::Split);
assert_eq!(
resolve_layered([&overlay, &base], &ctrl('s')),
Some(&Action::Split)
);
assert_eq!(
resolve_layered([&overlay, &base], &ctrl('q')),
Some(&Action::Quit)
);
assert_eq!(resolve_layered([&base], &ctrl('s')), Some(&Action::Save));
}
#[test]
fn layered_resolution_misses_when_no_layer_binds() {
let base: Keymap<Action> = Keymap::new();
assert_eq!(resolve_layered([&base], &ctrl('x')), None);
assert_eq!(
resolve_layered(std::iter::empty::<&Keymap<Action>>(), &ctrl('x')),
None
);
}
}