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