termusiclib/config/v2/tui/keys/
conflict.rs

1use ahash::HashMap;
2
3use super::KeyBinding;
4
5/// Stack to keep track of what path / field we are currently in
6#[derive(Debug, Clone, PartialEq)]
7pub(super) struct KeyPath(Vec<&'static str>);
8
9impl KeyPath {
10    pub fn new() -> Self {
11        Self(Vec::new())
12    }
13
14    pub fn new_with_toplevel(value: &'static str) -> Self {
15        let mut ret = Self::new();
16        ret.push(value);
17
18        ret
19    }
20
21    /// Push a new field onto the path
22    pub fn push(&mut self, value: &'static str) {
23        self.0.push(value);
24    }
25
26    /// Pop the last field from the path
27    pub fn pop(&mut self) -> Option<&'static str> {
28        self.0.pop()
29    }
30
31    /// Convert the currently stored path to a string plus a extra value, joined via `.`
32    pub fn join_with_field(&self, field: &'static str) -> String {
33        let mut ret = self.0.join(".");
34
35        ret.push('.');
36        ret.push_str(field);
37
38        ret
39    }
40}
41
42/// Error for when [`KeyBinding`] has a conflict with another key
43#[derive(Debug, Clone, PartialEq, thiserror::Error)]
44#[error("Key Conflict: '{key_path_first}' and '{key_path_second}', key: '{key}'")]
45pub struct KeyConflictError {
46    pub key_path_first: String,
47    pub key_path_second: String,
48    pub key: KeyBinding,
49}
50
51pub(super) type KeyHashMap = HashMap<&'static KeyBinding, &'static str>;
52pub(super) type KeyHashMapOwned = HashMap<KeyBinding, String>;
53
54pub(super) trait CheckConflict {
55    /// Iterator over all the individual keys
56    ///
57    /// Returns `(key, key_path_name)`
58    ///
59    /// Only for direct keys
60    fn iter(&self) -> impl Iterator<Item = (&KeyBinding, &'static str)>;
61    /// Check for key conflicts with current instance and against `global_keys`
62    fn check_conflict(
63        &self,
64        key_path: &mut KeyPath,
65        global_keys: &mut KeyHashMapOwned,
66    ) -> Result<(), Vec<KeyConflictError>>;
67}
68
69/// Macro to not repeat yourself writing `once(...).chain(once(...))`
70///
71/// Allows usage of calling one at a time:
72///
73/// ```
74/// once_chain!((&self.escape, "escape"))
75/// ```
76///
77/// or multiple at a time to even save repeated `once_chain!` invocations:
78///
79/// ```
80/// once_chain! {
81///     (&self.escape, "escape"),
82///     (&self.quit, "quit"),
83/// }
84/// ```
85#[macro_export]
86macro_rules! once_chain {
87    (
88        $first:expr
89        $(
90            , $second:expr
91        )* $(,)?
92    ) => {
93        std::iter::once($first)
94        $(.chain(std::iter::once($second)))*
95    }
96}