use std::fmt;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Help {
pub key: String,
pub desc: String,
}
impl Help {
#[must_use]
pub fn new(key: impl Into<String>, desc: impl Into<String>) -> Self {
Self {
key: key.into(),
desc: desc.into(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Binding {
keys: Vec<String>,
help: Help,
disabled: bool,
}
impl Binding {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn keys(mut self, keys: &[&str]) -> Self {
self.keys = keys.iter().map(|&s| s.to_string()).collect();
self
}
#[must_use]
pub fn help(mut self, key: impl Into<String>, desc: impl Into<String>) -> Self {
self.help = Help::new(key, desc);
self
}
#[must_use]
pub fn disabled(mut self) -> Self {
self.disabled = true;
self
}
pub fn set_keys(&mut self, keys: &[&str]) {
self.keys = keys.iter().map(|&s| s.to_string()).collect();
}
#[must_use]
pub fn get_keys(&self) -> &[String] {
&self.keys
}
pub fn set_help(&mut self, key: impl Into<String>, desc: impl Into<String>) {
self.help = Help::new(key, desc);
}
#[must_use]
pub fn get_help(&self) -> &Help {
&self.help
}
#[must_use]
pub fn enabled(&self) -> bool {
!self.disabled && !self.keys.is_empty()
}
pub fn enable(&mut self, enabled: bool) {
self.disabled = !enabled;
}
#[must_use]
pub fn set_enabled(mut self, enabled: bool) -> Self {
self.disabled = !enabled;
self
}
pub fn unbind(&mut self) {
self.keys.clear();
self.help = Help::default();
}
}
pub fn matches<K: fmt::Display>(key: K, bindings: &[&Binding]) -> bool {
let key_str = key.to_string();
for binding in bindings {
if binding.enabled() {
for k in &binding.keys {
if *k == key_str {
return true;
}
}
}
}
false
}
pub fn matches_one<K: fmt::Display>(key: K, binding: &Binding) -> bool {
matches(key, &[binding])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_binding_new() {
let binding = Binding::new();
assert!(binding.get_keys().is_empty());
assert!(!binding.enabled());
}
#[test]
fn test_binding_with_keys() {
let binding = Binding::new().keys(&["k", "up"]);
assert_eq!(binding.get_keys(), &["k", "up"]);
assert!(binding.enabled());
}
#[test]
fn test_binding_with_help() {
let binding = Binding::new()
.keys(&["q"])
.help("q", "quit the application");
assert_eq!(binding.get_help().key, "q");
assert_eq!(binding.get_help().desc, "quit the application");
}
#[test]
fn test_binding_disabled() {
let binding = Binding::new().keys(&["q"]).disabled();
assert!(!binding.enabled());
}
#[test]
fn test_binding_set_enabled() {
let mut binding = Binding::new().keys(&["q"]).disabled();
assert!(!binding.enabled());
binding.enable(true);
assert!(binding.enabled());
}
#[test]
fn test_binding_set_enabled_builder() {
let binding = Binding::new().keys(&["q"]).set_enabled(false);
assert!(!binding.enabled());
let binding = binding.set_enabled(true);
assert!(binding.enabled());
}
#[test]
fn test_binding_unbind() {
let mut binding = Binding::new().keys(&["q"]).help("q", "quit");
binding.unbind();
assert!(binding.get_keys().is_empty());
assert!(binding.get_help().key.is_empty());
}
#[test]
fn test_matches() {
let up = Binding::new().keys(&["k", "up"]);
let down = Binding::new().keys(&["j", "down"]);
assert!(matches("k", &[&up, &down]));
assert!(matches("up", &[&up, &down]));
assert!(matches("j", &[&up, &down]));
assert!(matches("down", &[&up, &down]));
assert!(!matches("x", &[&up, &down]));
}
#[test]
fn test_matches_disabled() {
let binding = Binding::new().keys(&["q"]).disabled();
assert!(!matches("q", &[&binding]));
}
#[test]
fn test_matches_empty() {
let binding = Binding::new();
assert!(!matches("q", &[&binding]));
}
#[test]
fn test_matches_one() {
let quit = Binding::new().keys(&["q", "ctrl+c"]);
assert!(matches_one("q", &quit));
assert!(matches_one("ctrl+c", &quit));
assert!(!matches_one("x", &quit));
}
}