1use crate::{Action, DispatchContext, DispatchContextPredicate, KeyMatch, Keystroke};
2use anyhow::Result;
3use smallvec::SmallVec;
4
5pub struct KeyBinding {
6 action: Box<dyn Action>,
7 pub(super) keystrokes: SmallVec<[Keystroke; 2]>,
8 pub(super) context_predicate: Option<DispatchContextPredicate>,
9}
10
11impl KeyBinding {
12 pub fn new<A: Action>(keystrokes: &str, action: A, context_predicate: Option<&str>) -> Self {
13 Self::load(keystrokes, Box::new(action), context_predicate).unwrap()
14 }
15
16 pub fn load(keystrokes: &str, action: Box<dyn Action>, context: Option<&str>) -> Result<Self> {
17 let context = if let Some(context) = context {
18 Some(DispatchContextPredicate::parse(context)?)
19 } else {
20 None
21 };
22
23 let keystrokes = keystrokes
24 .split_whitespace()
25 .map(Keystroke::parse)
26 .collect::<Result<_>>()?;
27
28 Ok(Self {
29 keystrokes,
30 action,
31 context_predicate: context,
32 })
33 }
34
35 pub fn matches_context(&self, contexts: &[&DispatchContext]) -> bool {
36 self.context_predicate
37 .as_ref()
38 .map(|predicate| predicate.eval(contexts))
39 .unwrap_or(true)
40 }
41
42 pub fn match_keystrokes(
43 &self,
44 pending_keystrokes: &[Keystroke],
45 contexts: &[&DispatchContext],
46 ) -> KeyMatch {
47 if self.keystrokes.as_ref().starts_with(&pending_keystrokes)
48 && self.matches_context(contexts)
49 {
50 // If the binding is completed, push it onto the matches list
51 if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
52 KeyMatch::Some(self.action.boxed_clone())
53 } else {
54 KeyMatch::Pending
55 }
56 } else {
57 KeyMatch::None
58 }
59 }
60
61 pub fn keystrokes_for_action(
62 &self,
63 action: &dyn Action,
64 contexts: &[&DispatchContext],
65 ) -> Option<SmallVec<[Keystroke; 2]>> {
66 if self.action.partial_eq(action) && self.matches_context(contexts) {
67 Some(self.keystrokes.clone())
68 } else {
69 None
70 }
71 }
72
73 pub fn keystrokes(&self) -> &[Keystroke] {
74 self.keystrokes.as_slice()
75 }
76
77 pub fn action(&self) -> &dyn Action {
78 self.action.as_ref()
79 }
80}