Skip to main content

hyprshell_config_lib/
modifier.rs

1use anyhow::bail;
2use serde::de::Visitor;
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use std::fmt;
5
6#[derive(Debug, Clone, Copy, Eq, PartialEq)]
7pub enum Modifier {
8    Alt,
9    Ctrl,
10    Super,
11    None,
12}
13
14#[allow(clippy::must_use_candidate)]
15impl Modifier {
16    pub fn to_l_key(&self) -> String {
17        self.to_keysym_l().to_string()
18    }
19    pub const fn to_keysym_l(&self) -> &'static str {
20        // XKB keysym names (xkbcommon-keysyms.h). xkbcommon's case-insensitive
21        // lookup accepts e.g. "alt_l", but "ctrl_l" is NOT a valid XKB keysym
22        // (the canonical name is "Control_L"). So we return canonical XKB names.
23        match self {
24            Self::Alt => "Alt_L",
25            Self::Ctrl => "Control_L",
26            Self::Super => "Super_L",
27            Self::None => "",
28        }
29    }
30    pub const fn to_keysym_r(&self) -> &'static str {
31        match self {
32            Self::Alt => "Alt_R",
33            Self::Ctrl => "Control_R",
34            Self::Super => "Super_R",
35            Self::None => "",
36        }
37    }
38    pub const fn to_str(&self) -> &'static str {
39        match self {
40            Self::Alt => "alt",
41            Self::Ctrl => "ctrl",
42            Self::Super => "super",
43            Self::None => "none",
44        }
45    }
46}
47
48impl<'de> Deserialize<'de> for Modifier {
49    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
50    where
51        D: Deserializer<'de>,
52    {
53        struct ModVisitor;
54        impl Visitor<'_> for ModVisitor {
55            type Value = Modifier;
56            fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
57                fmt.write_str("one of: alt, ctrl, super, none")
58            }
59            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
60            where
61                E: serde::de::Error,
62            {
63                value
64                    .try_into()
65                    .map_err(|_e| E::unknown_variant(value, &["alt", "ctrl", "super", "none"]))
66            }
67        }
68        deserializer.deserialize_str(ModVisitor)
69    }
70}
71
72impl Serialize for Modifier {
73    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
74    where
75        S: Serializer,
76    {
77        let s = self.to_str();
78        serializer.serialize_str(s)
79    }
80}
81
82impl TryFrom<&str> for Modifier {
83    type Error = anyhow::Error;
84
85    fn try_from(value: &str) -> Result<Self, Self::Error> {
86        match value.to_ascii_lowercase().as_str() {
87            "alt" => Ok(Self::Alt),
88            "ctrl" | "control" => Ok(Self::Ctrl),
89            "super" | "win" | "windows" | "meta" => Ok(Self::Super),
90            "none" | "" => Ok(Self::None),
91            other => bail!("Invalid modifier: {other}"),
92        }
93    }
94}
95
96impl fmt::Display for Modifier {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        match self {
99            Self::Alt => write!(f, "Alt"),
100            Self::Ctrl => write!(f, "Ctrl"),
101            Self::Super => write!(f, "Super"),
102            Self::None => write!(f, "None"),
103        }
104    }
105}