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)))*
}
}