inquire/prompts/
action.rs

1//! Definitions for the broad Action type which encompasses
2//! the directives for prompts.
3
4use std::fmt::Debug;
5
6use crate::ui::{Key, KeyModifiers};
7
8/// Top-level type to describe the directives a prompt
9/// receives.
10///
11/// Each prompt should implement its own custom InnerAction type
12/// which is parsed and stored in the Inner variant, if applicable,
13/// on the normal execution flow of a prompt.
14#[derive(Copy, Clone, Debug, PartialEq, Eq)]
15pub enum Action<I>
16where
17    I: Copy + Clone + PartialEq + Eq,
18{
19    /// Submits the current prompt answer, finishing the prompt if valid.
20    Submit,
21    /// Cancels the prompt execution with a graceful shutdown.
22    Cancel,
23    /// Interrupts the prompt execution without a graceful shutdown.
24    Interrupt,
25    /// Specialized actions according to the prompt type.
26    Inner(I),
27}
28
29impl<I> Action<I>
30where
31    I: Copy + Clone + PartialEq + Eq,
32{
33    /// Derives a prompt action from a Key event.
34    pub fn from_key<C>(key: Key, config: &C) -> Option<Action<I>>
35    where
36        I: InnerAction<Config = C>,
37    {
38        match key {
39            Key::Enter
40            | Key::Char('\n', KeyModifiers::NONE)
41            | Key::Char('j', KeyModifiers::CONTROL) => Some(Action::Submit),
42            Key::Escape | Key::Char('g' | 'd', KeyModifiers::CONTROL) => Some(Action::Cancel),
43            Key::Char('c', KeyModifiers::CONTROL) => Some(Action::Interrupt),
44            key => I::from_key(key, config).map(Action::Inner),
45        }
46    }
47}
48
49/// InnerActions are specialized prompt actions.
50///
51/// They must provide an implementation to optionally derive an action
52/// from a key event.
53pub trait InnerAction
54where
55    Self: Sized + Copy + Clone + PartialEq + Eq,
56{
57    /// Configuration type for the prompt.
58    ///
59    /// This is used to derive the action from a key event.
60    type Config;
61
62    /// Derives a prompt action from a Key event and the prompt configuration.
63    fn from_key(key: Key, config: &Self::Config) -> Option<Self>
64    where
65        Self: Sized;
66}
67
68#[cfg(test)]
69mod test {
70    use crate::{
71        ui::{Key, KeyModifiers},
72        Action, InnerAction,
73    };
74
75    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
76    pub enum MockInnerAction {
77        Action(Key),
78    }
79
80    impl InnerAction for MockInnerAction {
81        type Config = ();
82
83        fn from_key(key: Key, _config: &()) -> Option<Self>
84        where
85            Self: Sized,
86        {
87            Some(Self::Action(key))
88        }
89    }
90
91    #[test]
92    fn standard_keybindings_for_submit() {
93        let key = Key::Enter;
94        assert_eq!(
95            Some(Action::<MockInnerAction>::Submit),
96            Action::from_key(key, &())
97        );
98    }
99
100    #[test]
101    fn standard_keybindings_for_cancel() {
102        let key = Key::Escape;
103        assert_eq!(
104            Some(Action::<MockInnerAction>::Cancel),
105            Action::from_key(key, &())
106        );
107    }
108
109    #[test]
110    fn ctrl_c_results_in_interrupt_action() {
111        let key = Key::Char('c', KeyModifiers::CONTROL);
112        assert_eq!(
113            Some(Action::<MockInnerAction>::Interrupt),
114            Action::from_key(key, &())
115        );
116    }
117
118    #[test]
119    fn generic_keys_are_passed_down_to_inner_action() {
120        assert_eq!(
121            Some(Action::<MockInnerAction>::Inner(MockInnerAction::Action(
122                Key::Char('a', KeyModifiers::NONE)
123            ))),
124            Action::from_key(Key::Char('a', KeyModifiers::NONE), &())
125        );
126        assert_eq!(
127            Some(Action::<MockInnerAction>::Inner(MockInnerAction::Action(
128                Key::Home
129            ))),
130            Action::from_key(Key::Home, &())
131        );
132        assert_eq!(
133            Some(Action::<MockInnerAction>::Inner(MockInnerAction::Action(
134                Key::PageDown(KeyModifiers::NONE)
135            ))),
136            Action::from_key(Key::PageDown(KeyModifiers::NONE), &())
137        );
138    }
139
140    #[test]
141    fn emacs_control_keybindings() {
142        assert_eq!(
143            Some(Action::<MockInnerAction>::Submit),
144            Action::from_key(Key::Char('j', KeyModifiers::CONTROL), &())
145        );
146        assert_eq!(
147            Some(Action::<MockInnerAction>::Cancel),
148            Action::from_key(Key::Char('g', KeyModifiers::CONTROL), &())
149        );
150    }
151}