Skip to main content

zellij_utils/input/
theme.rs

1use serde::{
2    de::{Error, Visitor},
3    Deserialize, Deserializer, Serialize, Serializer,
4};
5use std::{
6    collections::{BTreeMap, HashMap},
7    fmt,
8};
9
10use crate::data::Styling;
11
12#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)]
13pub struct UiConfig {
14    pub pane_frames: FrameConfig,
15}
16
17impl UiConfig {
18    pub fn merge(&self, other: UiConfig) -> Self {
19        let mut merged = self.clone();
20        merged.pane_frames = merged.pane_frames.merge(other.pane_frames);
21        merged
22    }
23}
24
25#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)]
26pub struct FrameConfig {
27    pub rounded_corners: bool,
28    pub hide_session_name: bool,
29}
30
31impl FrameConfig {
32    pub fn merge(&self, other: FrameConfig) -> Self {
33        let mut merged = self.clone();
34        merged.rounded_corners = other.rounded_corners;
35        merged.hide_session_name = other.hide_session_name;
36        merged
37    }
38}
39
40#[derive(Clone, PartialEq, Default, Serialize, Deserialize)]
41pub struct Themes(HashMap<String, Theme>);
42
43impl fmt::Debug for Themes {
44    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45        let mut stable_sorted = BTreeMap::new();
46        for (theme_name, theme) in self.0.iter() {
47            stable_sorted.insert(theme_name, theme);
48        }
49        write!(f, "{:#?}", stable_sorted)
50    }
51}
52
53impl Themes {
54    pub fn from_data(theme_data: HashMap<String, Theme>) -> Self {
55        Themes(theme_data)
56    }
57    pub fn insert(&mut self, theme_name: String, theme: Theme) {
58        self.0.insert(theme_name, theme);
59    }
60    pub fn merge(&self, mut other: Themes) -> Self {
61        let mut merged = self.clone();
62        for (name, theme) in other.0.drain() {
63            merged.0.insert(name, theme);
64        }
65        merged
66    }
67    pub fn get_theme(&self, theme_name: &str) -> Option<&Theme> {
68        self.0.get(theme_name)
69    }
70    pub fn inner(&self) -> &HashMap<String, Theme> {
71        &self.0
72    }
73}
74
75#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
76pub struct Theme {
77    pub sourced_from_external_file: bool,
78    #[serde(flatten)]
79    pub palette: Styling,
80}
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
83pub struct HexColor(u8, u8, u8);
84
85impl From<HexColor> for (u8, u8, u8) {
86    fn from(e: HexColor) -> (u8, u8, u8) {
87        let HexColor(r, g, b) = e;
88        (r, g, b)
89    }
90}
91
92pub struct HexColorVisitor();
93
94impl<'de> Visitor<'de> for HexColorVisitor {
95    type Value = HexColor;
96
97    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
98        write!(formatter, "a hex color in the format #RGB or #RRGGBB")
99    }
100
101    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
102    where
103        E: Error,
104    {
105        if let Some(stripped) = s.strip_prefix('#') {
106            return self.visit_str(stripped);
107        }
108
109        if s.len() == 3 {
110            Ok(HexColor(
111                u8::from_str_radix(&s[0..1], 16).map_err(E::custom)? * 0x11,
112                u8::from_str_radix(&s[1..2], 16).map_err(E::custom)? * 0x11,
113                u8::from_str_radix(&s[2..3], 16).map_err(E::custom)? * 0x11,
114            ))
115        } else if s.len() == 6 {
116            Ok(HexColor(
117                u8::from_str_radix(&s[0..2], 16).map_err(E::custom)?,
118                u8::from_str_radix(&s[2..4], 16).map_err(E::custom)?,
119                u8::from_str_radix(&s[4..6], 16).map_err(E::custom)?,
120            ))
121        } else {
122            Err(Error::custom(
123                "Hex color must be of form \"#RGB\" or \"#RRGGBB\"",
124            ))
125        }
126    }
127}
128
129impl<'de> Deserialize<'de> for HexColor {
130    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
131    where
132        D: Deserializer<'de>,
133    {
134        deserializer.deserialize_str(HexColorVisitor())
135    }
136}
137impl Serialize for HexColor {
138    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139    where
140        S: Serializer,
141    {
142        serializer.serialize_str(format!("{:02X}{:02X}{:02X}", self.0, self.1, self.2).as_str())
143    }
144}
145
146#[cfg(test)]
147#[path = "./unit/theme_test.rs"]
148mod theme_test;