bitcoin_terminal_dashboard/app/
actions.rs

1use std::collections::HashMap;
2use std::fmt::{self, Display};
3use std::slice::Iter;
4
5use crate::inputs::key::Key;
6
7/// We define all available action
8#[derive(Debug, Clone, Copy, Eq, PartialEq)]
9pub enum Action {
10    Quit,
11}
12
13impl Action {
14    /// All available actions
15    pub fn iterator() -> Iter<'static, Action> {
16        static ACTIONS: [Action; 1] = [Action::Quit];
17        ACTIONS.iter()
18    }
19
20    /// List of key associated to action
21    pub fn keys(&self) -> &[Key] {
22        match self {
23            Action::Quit => &[Key::Ctrl('c'), Key::Char('q')],
24        }
25    }
26}
27
28/// Could display a user friendly short description of action
29impl Display for Action {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        let str = match self {
32            Action::Quit => "Quit",
33        };
34        write!(f, "{}", str)
35    }
36}
37
38/// The application should have some contextual actions.
39#[derive(Default, Debug, Clone)]
40pub struct Actions(Vec<Action>);
41
42impl Actions {
43    /// Given a key, find the corresponding action
44    pub fn find(&self, key: Key) -> Option<&Action> {
45        Action::iterator()
46            .filter(|action| self.0.contains(action))
47            .find(|action| action.keys().contains(&key))
48    }
49
50    /// Get contextual actions.
51    /// (just for building a help view)
52    pub fn actions(&self) -> &[Action] {
53        self.0.as_slice()
54    }
55}
56
57impl From<Vec<Action>> for Actions {
58    /// Build contextual action
59    ///
60    /// # Panics
61    ///
62    /// If two actions have same key
63    fn from(actions: Vec<Action>) -> Self {
64        // Check key unicity
65        let mut map: HashMap<Key, Vec<Action>> = HashMap::new();
66        for action in actions.iter() {
67            for key in action.keys().iter() {
68                match map.get_mut(key) {
69                    Some(vec) => vec.push(*action),
70                    None => {
71                        map.insert(*key, vec![*action]);
72                    }
73                }
74            }
75        }
76        let errors = map
77            .iter()
78            .filter(|(_, actions)| actions.len() > 1) // at least two actions share same shortcut
79            .map(|(key, actions)| {
80                let actions = actions
81                    .iter()
82                    .map(Action::to_string)
83                    .collect::<Vec<_>>()
84                    .join(", ");
85                format!("Conflict key {} with actions {}", key, actions)
86            })
87            .collect::<Vec<_>>();
88        if !errors.is_empty() {
89            panic!("{}", errors.join("; "))
90        }
91
92        // Ok, we can create contextual actions
93        Self(actions)
94    }
95}