1use serde::{Deserialize, Deserializer, de};
2use std::str::FromStr;
3
4#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct Style {
6 pub fg: Option<Color>,
7 pub bg: Option<Color>,
8 pub add_modifier: Modifier,
9 pub sub_modifier: Modifier,
10}
11
12impl Style {
13 pub const fn new() -> Self {
14 Style {
15 fg: None,
16 bg: None,
17 add_modifier: Modifier::empty(),
18 sub_modifier: Modifier::empty(),
19 }
20 }
21
22 pub fn patch(mut self, other: Style) -> Style {
24 self.fg = other.fg.or(self.fg);
25 self.bg = other.bg.or(self.bg);
26
27 self.add_modifier.remove(other.sub_modifier);
28 self.add_modifier.insert(other.add_modifier);
29 self.sub_modifier.remove(other.add_modifier);
30 self.sub_modifier.insert(other.sub_modifier);
31
32 self
33 }
34}
35
36#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
37pub enum Color {
38 #[default]
39 Reset,
40 Black,
41 Red,
42 Green,
43 Yellow,
44 Blue,
45 Magenta,
46 Cyan,
47 Gray,
48 DarkGray,
49 LightRed,
50 LightGreen,
51 LightYellow,
52 LightBlue,
53 LightMagenta,
54 LightCyan,
55 White,
56 Rgb(u8, u8, u8),
57 Indexed(u8),
58}
59
60impl From<Color> for crossterm::style::Color {
61 fn from(color: Color) -> Self {
62 match color {
63 Color::Reset => Self::Reset,
64 Color::Black => Self::Black,
65 Color::Red => Self::DarkRed,
66 Color::Green => Self::DarkGreen,
67 Color::Yellow => Self::DarkYellow,
68 Color::Blue => Self::DarkBlue,
69 Color::Magenta => Self::DarkMagenta,
70 Color::Cyan => Self::DarkCyan,
71 Color::Gray => Self::Grey,
72 Color::DarkGray => Self::DarkGrey,
73 Color::LightRed => Self::Red,
74 Color::LightGreen => Self::Green,
75 Color::LightYellow => Self::Yellow,
76 Color::LightBlue => Self::Blue,
77 Color::LightMagenta => Self::Magenta,
78 Color::LightCyan => Self::Cyan,
79 Color::White => Self::White,
80 Color::Rgb(r, g, b) => Self::Rgb { r, g, b },
81 Color::Indexed(i) => Self::AnsiValue(i),
82 }
83 }
84}
85
86impl FromStr for Color {
87 type Err = ParseColorError;
88
89 fn from_str(s: &str) -> Result<Self, Self::Err> {
90 let name = s
93 .to_lowercase()
94 .replace([' ', '-', '_'], "")
95 .replace("bright", "light")
96 .replace("grey", "gray")
97 .replace("silver", "gray")
98 .replace("lightblack", "darkgray")
99 .replace("lightwhite", "white")
100 .replace("lightgray", "white");
101
102 Ok(match name.as_str() {
103 "reset" => Self::Reset,
104 "black" => Self::Black,
105 "red" => Self::Red,
106 "green" => Self::Green,
107 "yellow" => Self::Yellow,
108 "blue" => Self::Blue,
109 "magenta" => Self::Magenta,
110 "cyan" => Self::Cyan,
111 "gray" => Self::Gray,
112 "darkgray" => Self::DarkGray,
113 "lightred" => Self::LightRed,
114 "lightgreen" => Self::LightGreen,
115 "lightyellow" => Self::LightYellow,
116 "lightblue" => Self::LightBlue,
117 "lightmagenta" => Self::LightMagenta,
118 "lightcyan" => Self::LightCyan,
119 "white" => Self::White,
120 _ => {
121 if let Ok(index) = s.parse::<u8>() {
122 Self::Indexed(index)
123 } else if let Some((r, g, b)) = parse_hex_color(s) {
124 Self::Rgb(r, g, b)
125 } else {
126 return Err(ParseColorError);
127 }
128 }
129 })
130 }
131}
132
133fn parse_hex_color(s: &str) -> Option<(u8, u8, u8)> {
134 let hex = s.strip_prefix('#')?;
135 if hex.len() != 6 {
136 return None;
137 }
138
139 let channel = |range: std::ops::Range<usize>| u8::from_str_radix(hex.get(range)?, 16).ok();
141 Some((channel(0..2)?, channel(2..4)?, channel(4..6)?))
142}
143
144#[derive(Debug)]
145pub struct ParseColorError;
146
147impl std::fmt::Display for ParseColorError {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 f.write_str("invalid color")
150 }
151}
152
153impl std::error::Error for ParseColorError {}
154
155impl<'de> Deserialize<'de> for Color {
156 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
157 let s = String::deserialize(deserializer)?;
158 Color::from_str(&s).map_err(|_| de::Error::custom(format!("unknown color: {s}")))
159 }
160}
161
162#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
163pub struct Modifier(pub u16);
164
165impl Modifier {
166 pub const BOLD: Self = Modifier(0b0000_0000_0001);
167 pub const DIM: Self = Modifier(0b0000_0000_0010);
168 pub const ITALIC: Self = Modifier(0b0000_0000_0100);
169 pub const UNDERLINED: Self = Modifier(0b0000_0000_1000);
170 pub const SLOW_BLINK: Self = Modifier(0b0000_0001_0000);
171 pub const RAPID_BLINK: Self = Modifier(0b0000_0010_0000);
172 pub const REVERSED: Self = Modifier(0b0000_0100_0000);
173 pub const HIDDEN: Self = Modifier(0b0000_1000_0000);
174 pub const CROSSED_OUT: Self = Modifier(0b0001_0000_0000);
175
176 const NAMED: &'static [(&'static str, Self)] = &[
177 ("BOLD", Self::BOLD),
178 ("DIM", Self::DIM),
179 ("ITALIC", Self::ITALIC),
180 ("UNDERLINED", Self::UNDERLINED),
181 ("SLOW_BLINK", Self::SLOW_BLINK),
182 ("RAPID_BLINK", Self::RAPID_BLINK),
183 ("REVERSED", Self::REVERSED),
184 ("HIDDEN", Self::HIDDEN),
185 ("CROSSED_OUT", Self::CROSSED_OUT),
186 ];
187
188 pub const fn empty() -> Self {
189 Modifier(0)
190 }
191
192 pub fn contains(self, other: Self) -> bool {
193 self.0 & other.0 == other.0
194 }
195
196 pub fn insert(&mut self, other: Self) {
197 self.0 |= other.0;
198 }
199
200 pub fn remove(&mut self, other: Self) {
201 self.0 &= !other.0;
202 }
203}
204
205impl std::ops::BitOr for Modifier {
206 type Output = Self;
207
208 fn bitor(self, other: Self) -> Self {
209 Modifier(self.0 | other.0)
210 }
211}
212
213impl std::ops::BitOrAssign for Modifier {
214 fn bitor_assign(&mut self, other: Self) {
215 self.insert(other);
216 }
217}
218
219impl FromStr for Modifier {
220 type Err = ParseModifierError;
221
222 fn from_str(s: &str) -> Result<Self, Self::Err> {
223 let mut modifier = Modifier::empty();
224
225 if s.trim().is_empty() {
226 return Ok(modifier);
227 }
228
229 for name in s.split('|') {
230 let name = name.trim();
231 let (_, flag) = Modifier::NAMED
232 .iter()
233 .find(|(named, _)| named.eq_ignore_ascii_case(name))
234 .ok_or(ParseModifierError)?;
235
236 modifier.insert(*flag);
237 }
238
239 Ok(modifier)
240 }
241}
242
243#[derive(Debug)]
244pub struct ParseModifierError;
245
246impl std::fmt::Display for ParseModifierError {
247 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248 f.write_str("invalid modifier")
249 }
250}
251
252impl std::error::Error for ParseModifierError {}
253
254impl<'de> Deserialize<'de> for Modifier {
255 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
256 let s = String::deserialize(deserializer)?;
257 Modifier::from_str(&s).map_err(|_| de::Error::custom(format!("unknown modifier: {s}")))
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn parse_modifiers() {
267 assert_eq!(Modifier::from_str("").unwrap(), Modifier::empty());
268 assert_eq!(Modifier::from_str("BOLD").unwrap(), Modifier::BOLD);
269
270 let mut both = Modifier::BOLD;
271 both.insert(Modifier::ITALIC);
272 assert_eq!(Modifier::from_str("BOLD|ITALIC").unwrap(), both);
273 assert_eq!(Modifier::from_str("BOLD | ITALIC").unwrap(), both);
274
275 assert!(Modifier::from_str("NONSENSE").is_err());
276 }
277
278 #[test]
279 fn parse_colors() {
280 assert_eq!(Color::from_str("light blue").unwrap(), Color::LightBlue);
281 assert_eq!(Color::from_str("LightBlue").unwrap(), Color::LightBlue);
282 assert_eq!(Color::from_str("bright-blue").unwrap(), Color::LightBlue);
283 assert_eq!(Color::from_str("grey").unwrap(), Color::Gray);
284 assert_eq!(Color::from_str("reset").unwrap(), Color::Reset);
285 assert_eq!(Color::from_str("255").unwrap(), Color::Indexed(255));
286 assert_eq!(
287 Color::from_str("#707070").unwrap(),
288 Color::Rgb(112, 112, 112)
289 );
290
291 assert!(Color::from_str("#70707").is_err());
292 assert!(Color::from_str("#€abc").is_err());
293 assert!(Color::from_str("nonsense").is_err());
294 }
295}