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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use std::{error::Error, fmt::Display};

use ahash::HashMap;

use super::KeyBinding;

/// Stack to keep track of what path / field we are currently in
#[derive(Debug, Clone, PartialEq)]
pub(super) struct KeyPath(Vec<&'static str>);

impl KeyPath {
    pub fn new() -> Self {
        Self(Vec::new())
    }

    pub fn new_with_toplevel(value: &'static str) -> Self {
        let mut ret = Self::new();
        ret.push(value);

        ret
    }

    /// Push a new field onto the path
    pub fn push(&mut self, value: &'static str) {
        self.0.push(value);
    }

    /// Pop the last field from the path
    pub fn pop(&mut self) -> Option<&'static str> {
        self.0.pop()
    }

    /// Convert the currently stored path to a string plus a extra value, joined via `.`
    pub fn join_with_field(&self, field: &'static str) -> String {
        let mut ret = self.0.join(".");

        ret.push('.');
        ret.push_str(field);

        ret
    }
}

// TODO: upgrade errors with what config-key has errored
// TODO: consider upgrading this with "thiserror"
/// Error for when [`Key`] parsing fails
#[derive(Debug, Clone, PartialEq)]
pub struct KeyConflictError {
    pub key_path_first: String,
    pub key_path_second: String,
    pub key: KeyBinding,
}

impl Display for KeyConflictError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Key Conflict: '{}' and '{}', key: '{}'",
            self.key_path_first, self.key_path_second, self.key
        )
    }
}

impl Error for KeyConflictError {}

pub(super) type KeyHashMap = HashMap<&'static KeyBinding, &'static str>;
pub(super) type KeyHashMapOwned = HashMap<KeyBinding, String>;

pub(super) trait CheckConflict {
    /// Iterator over all the individual keys
    ///
    /// Returns `(key, key_path_name)`
    ///
    /// Only for direct keys
    fn iter(&self) -> impl Iterator<Item = (&KeyBinding, &'static str)>;
    /// Check for key conflicts with current instance and against `global_keys`
    fn check_conflict(
        &self,
        key_path: &mut KeyPath,
        global_keys: &mut KeyHashMapOwned,
    ) -> Result<(), Vec<KeyConflictError>>;
}

/// Macro to not repeat yourself writing `once(...).chain(once(...))`
///
/// Allows usage of calling one at a time:
///
/// ```
/// const_str!(1 + 1)
/// ```
///
/// or multiple at a time to even save repeated `once_chain!` invokations:
///
/// ```
/// const_str! {
///     1 + 1,
///     2 + 2,
/// }
/// ```
#[macro_export]
macro_rules! once_chain {
    (
        $first:expr
        $(
            , $second:expr
        )* $(,)?
    ) => {
        std::iter::once($first)
        $(.chain(std::iter::once($second)))*
    }
}