1use std::{borrow::Cow, fmt::Display, str::FromStr};
2
3use bstr::{BStr, BString};
4
5use crate::{Color, Error};
6
7impl Display for Color {
8 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9 let mut write_space = None;
10 if let Some(fg) = self.foreground {
11 fg.fmt(f)?;
12 write_space = Some(());
13 }
14
15 if let Some(bg) = self.background {
16 if write_space.take().is_some() {
17 write!(f, " ")?;
18 }
19 bg.fmt(f)?;
20 write_space = Some(());
21 }
22
23 if !self.attributes.is_empty() {
24 if write_space.take().is_some() {
25 write!(f, " ")?;
26 }
27 self.attributes.fmt(f)?;
28 }
29 Ok(())
30 }
31}
32
33fn color_err(input: impl Into<BString>) -> Error {
34 Error::new(
35 "Colors are specific color values and their attributes, like 'brightred', or 'blue'",
36 input,
37 )
38}
39
40impl TryFrom<&BStr> for Color {
41 type Error = Error;
42
43 fn try_from(s: &BStr) -> Result<Self, Self::Error> {
44 let s = std::str::from_utf8(s).map_err(|err| color_err(s).with_err(err))?;
45 enum ColorItem {
46 Value(Name),
47 Attr(Attribute),
48 }
49
50 let items = s.split_whitespace().filter_map(|s| {
51 if s.is_empty() {
52 return None;
53 }
54
55 Some(
56 Name::from_str(s)
57 .map(ColorItem::Value)
58 .or_else(|_| Attribute::from_str(s).map(ColorItem::Attr)),
59 )
60 });
61
62 let mut foreground = None;
63 let mut background = None;
64 let mut attributes = Attribute::empty();
65 for item in items {
66 match item {
67 Ok(item) => match item {
68 ColorItem::Value(v) => {
69 if foreground.is_none() {
70 foreground = Some(v);
71 } else if background.is_none() {
72 background = Some(v);
73 } else {
74 return Err(color_err(s));
75 }
76 }
77 ColorItem::Attr(a) => attributes |= a,
78 },
79 Err(_) => return Err(color_err(s)),
80 }
81 }
82
83 Ok(Color {
84 foreground,
85 background,
86 attributes,
87 })
88 }
89}
90
91impl TryFrom<&str> for Color {
92 type Error = Error;
93
94 fn try_from(value: &str) -> Result<Self, Self::Error> {
95 Self::try_from(BStr::new(value))
96 }
97}
98
99impl TryFrom<Cow<'_, BStr>> for Color {
100 type Error = Error;
101
102 fn try_from(c: Cow<'_, BStr>) -> Result<Self, Self::Error> {
103 Self::try_from(c.as_ref())
104 }
105}
106
107impl TryFrom<BString> for Color {
108 type Error = Error;
109
110 fn try_from(value: BString) -> Result<Self, Self::Error> {
111 Self::try_from(BStr::new(&value))
112 }
113}
114
115#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
120pub enum Name {
121 Normal,
123 Default,
125 Black,
127 BrightBlack,
129 Red,
131 BrightRed,
133 Green,
135 BrightGreen,
137 Yellow,
139 BrightYellow,
141 Blue,
143 BrightBlue,
145 Magenta,
147 BrightMagenta,
149 Cyan,
151 BrightCyan,
153 White,
155 BrightWhite,
157 Ansi(
159 u8,
161 ),
162 Rgb(
164 u8,
166 u8,
168 u8,
170 ),
171}
172
173impl Display for Name {
174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175 match self {
176 Self::Normal => write!(f, "normal"),
177 Self::Default => write!(f, "default"),
178 Self::Black => write!(f, "black"),
179 Self::BrightBlack => write!(f, "brightblack"),
180 Self::Red => write!(f, "red"),
181 Self::BrightRed => write!(f, "brightred"),
182 Self::Green => write!(f, "green"),
183 Self::BrightGreen => write!(f, "brightgreen"),
184 Self::Yellow => write!(f, "yellow"),
185 Self::BrightYellow => write!(f, "brightyellow"),
186 Self::Blue => write!(f, "blue"),
187 Self::BrightBlue => write!(f, "brightblue"),
188 Self::Magenta => write!(f, "magenta"),
189 Self::BrightMagenta => write!(f, "brightmagenta"),
190 Self::Cyan => write!(f, "cyan"),
191 Self::BrightCyan => write!(f, "brightcyan"),
192 Self::White => write!(f, "white"),
193 Self::BrightWhite => write!(f, "brightwhite"),
194 Self::Ansi(num) => num.fmt(f),
195 Self::Rgb(r, g, b) => write!(f, "#{r:02x}{g:02x}{b:02x}"),
196 }
197 }
198}
199
200#[cfg(feature = "serde")]
201impl serde::Serialize for Name {
202 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
203 where
204 S: serde::Serializer,
205 {
206 serializer.serialize_str(&self.to_string())
207 }
208}
209
210impl FromStr for Name {
211 type Err = Error;
212
213 fn from_str(mut s: &str) -> Result<Self, Self::Err> {
214 let bright = if let Some(rest) = s.strip_prefix("bright") {
215 s = rest;
216 true
217 } else {
218 false
219 };
220
221 match s {
222 "normal" if !bright => return Ok(Self::Normal),
223 "-1" if !bright => return Ok(Self::Normal),
224 "normal" if bright => return Err(color_err(s)),
225 "default" if !bright => return Ok(Self::Default),
226 "default" if bright => return Err(color_err(s)),
227 "black" if !bright => return Ok(Self::Black),
228 "black" if bright => return Ok(Self::BrightBlack),
229 "red" if !bright => return Ok(Self::Red),
230 "red" if bright => return Ok(Self::BrightRed),
231 "green" if !bright => return Ok(Self::Green),
232 "green" if bright => return Ok(Self::BrightGreen),
233 "yellow" if !bright => return Ok(Self::Yellow),
234 "yellow" if bright => return Ok(Self::BrightYellow),
235 "blue" if !bright => return Ok(Self::Blue),
236 "blue" if bright => return Ok(Self::BrightBlue),
237 "magenta" if !bright => return Ok(Self::Magenta),
238 "magenta" if bright => return Ok(Self::BrightMagenta),
239 "cyan" if !bright => return Ok(Self::Cyan),
240 "cyan" if bright => return Ok(Self::BrightCyan),
241 "white" if !bright => return Ok(Self::White),
242 "white" if bright => return Ok(Self::BrightWhite),
243 _ => (),
244 }
245
246 if let Ok(v) = u8::from_str(s) {
247 return Ok(Self::Ansi(v));
248 }
249
250 if let Some(s) = s.strip_prefix('#') {
251 if s.len() == 6 && s.is_char_boundary(2) && s.is_char_boundary(4) && s.is_char_boundary(6) {
252 let rgb = (
253 u8::from_str_radix(&s[..2], 16),
254 u8::from_str_radix(&s[2..4], 16),
255 u8::from_str_radix(&s[4..], 16),
256 );
257
258 if let (Ok(r), Ok(g), Ok(b)) = rgb {
259 return Ok(Self::Rgb(r, g, b));
260 }
261 }
262 }
263
264 Err(color_err(s))
265 }
266}
267
268impl TryFrom<&BStr> for Name {
269 type Error = Error;
270
271 fn try_from(s: &BStr) -> Result<Self, Self::Error> {
272 Self::from_str(std::str::from_utf8(s).map_err(|err| color_err(s).with_err(err))?)
273 }
274}
275
276bitflags::bitflags! {
277 #[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
283 pub struct Attribute: u32 {
284 const BOLD = 1 << 1;
286 const DIM = 1 << 2;
288 const ITALIC = 1 << 3;
290 const UL = 1 << 4;
292 const BLINK = 1 << 5;
294 const REVERSE = 1 << 6;
296 const STRIKE = 1 << 7;
298 const RESET = 1 << 8;
300
301 const NO_DIM = 1 << 21;
303 const NO_BOLD = 1 << 22;
305 const NO_ITALIC = 1 << 23;
307 const NO_UL = 1 << 24;
309 const NO_BLINK = 1 << 25;
311 const NO_REVERSE = 1 << 26;
313 const NO_STRIKE = 1 << 27;
315 }
316}
317
318impl Display for Attribute {
319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320 let mut write_space = None;
321 for bit in 1..std::mem::size_of::<Attribute>() * 8 {
322 let attr = match Attribute::from_bits(1 << bit) {
323 Some(attr) => attr,
324 None => continue,
325 };
326 if self.contains(attr) {
327 if write_space.take().is_some() {
328 write!(f, " ")?;
329 }
330 match attr {
331 Attribute::RESET => write!(f, "reset"),
332 Attribute::BOLD => write!(f, "bold"),
333 Attribute::NO_BOLD => write!(f, "nobold"),
334 Attribute::DIM => write!(f, "dim"),
335 Attribute::NO_DIM => write!(f, "nodim"),
336 Attribute::UL => write!(f, "ul"),
337 Attribute::NO_UL => write!(f, "noul"),
338 Attribute::BLINK => write!(f, "blink"),
339 Attribute::NO_BLINK => write!(f, "noblink"),
340 Attribute::REVERSE => write!(f, "reverse"),
341 Attribute::NO_REVERSE => write!(f, "noreverse"),
342 Attribute::ITALIC => write!(f, "italic"),
343 Attribute::NO_ITALIC => write!(f, "noitalic"),
344 Attribute::STRIKE => write!(f, "strike"),
345 Attribute::NO_STRIKE => write!(f, "nostrike"),
346 _ => unreachable!("BUG: add new attribute flag"),
347 }?;
348 write_space = Some(());
349 }
350 }
351 Ok(())
352 }
353}
354
355#[cfg(feature = "serde")]
356impl serde::Serialize for Attribute {
357 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
358 where
359 S: serde::Serializer,
360 {
361 serializer.serialize_str(&self.to_string())
362 }
363}
364
365impl FromStr for Attribute {
366 type Err = Error;
367
368 fn from_str(mut s: &str) -> Result<Self, Self::Err> {
369 let inverted = if let Some(rest) = s.strip_prefix("no-").or_else(|| s.strip_prefix("no")) {
370 s = rest;
371 true
372 } else {
373 false
374 };
375
376 match s {
377 "reset" if !inverted => Ok(Attribute::RESET),
378 "reset" if inverted => Err(color_err(s)),
379 "bold" if !inverted => Ok(Attribute::BOLD),
380 "bold" if inverted => Ok(Attribute::NO_BOLD),
381 "dim" if !inverted => Ok(Attribute::DIM),
382 "dim" if inverted => Ok(Attribute::NO_DIM),
383 "ul" if !inverted => Ok(Attribute::UL),
384 "ul" if inverted => Ok(Attribute::NO_UL),
385 "blink" if !inverted => Ok(Attribute::BLINK),
386 "blink" if inverted => Ok(Attribute::NO_BLINK),
387 "reverse" if !inverted => Ok(Attribute::REVERSE),
388 "reverse" if inverted => Ok(Attribute::NO_REVERSE),
389 "italic" if !inverted => Ok(Attribute::ITALIC),
390 "italic" if inverted => Ok(Attribute::NO_ITALIC),
391 "strike" if !inverted => Ok(Attribute::STRIKE),
392 "strike" if inverted => Ok(Attribute::NO_STRIKE),
393 _ => Err(color_err(s)),
394 }
395 }
396}
397
398impl TryFrom<&BStr> for Attribute {
399 type Error = Error;
400
401 fn try_from(s: &BStr) -> Result<Self, Self::Error> {
402 Self::from_str(std::str::from_utf8(s).map_err(|err| color_err(s).with_err(err))?)
403 }
404}