1pub use std::borrow::Cow;
4use std::{
5 fmt::{self, Display, LowerHex},
6 mem,
7 str::FromStr,
8};
9
10#[cfg(feature = "crossterm")]
11use crossterm::style::{Attribute as CrossAttribute, Color as CrossColor};
12
13macro_rules! color_func {
15 ($funcname: ident, $color: path, $colorfunc: ident) => {
16 #[doc = concat!("A shorthand for `self.", stringify!($colorfunc), "(", stringify!($color), ")`")]
17 #[must_use = "its a builder pattern function"]
18 pub fn $funcname(self) -> Self {
19 self.$colorfunc($color)
20 }
21 }
22}
23
24macro_rules! modifier_func {
26 ($funcname: ident, $modifier: path, $attrfunc: ident) => {
27 #[doc = concat!("A shorthand for `self.width_modifier(", stringify!($modifier), ")`")]
28 #[must_use = "its a builder pattern function"]
29 pub fn $funcname(self) -> Self {
30 self.$attrfunc($modifier)
31 }
32 }
33}
34
35macro_rules! funcs {
37 () => {
38 color_func!(black, Color::Black, fg);
39 color_func!(red, Color::Red, fg);
40 color_func!(green, Color::Green, fg);
41 color_func!(yellow, Color::Yellow, fg);
42 color_func!(blue, Color::Blue, fg);
43 color_func!(magenta, Color::Magenta, fg);
44 color_func!(cyan, Color::Cyan, fg);
45 color_func!(white, Color::White, fg);
46
47 color_func!(bg_black, Color::Black, bg);
48 color_func!(bg_red, Color::Red, bg);
49 color_func!(bg_green, Color::Green, bg);
50 color_func!(bg_yellow, Color::Yellow, bg);
51 color_func!(bg_blue, Color::Blue, bg);
52 color_func!(bg_magenta, Color::Magenta, bg);
53 color_func!(bg_cyan, Color::Cyan, bg);
54 color_func!(bg_white, Color::White, bg);
55
56 color_func!(underline_colored_black, Color::Black, underline_color);
57 color_func!(underline_colored_red, Color::Red, underline_color);
58 color_func!(underline_colored_green, Color::Green, underline_color);
59 color_func!(underline_colored_yellow, Color::Yellow, underline_color);
60 color_func!(underline_colored_blue, Color::Blue, underline_color);
61 color_func!(underline_colored_magenta, Color::Magenta, underline_color);
62 color_func!(underline_colored_cyan, Color::Cyan, underline_color);
63 color_func!(underline_colored_white, Color::White, underline_color);
64
65 color_func!(bright_black, Color::BrightBlack, fg);
66 color_func!(bright_red, Color::BrightRed, fg);
67 color_func!(bright_green, Color::BrightGreen, fg);
68 color_func!(bright_yellow, Color::BrightYellow, fg);
69 color_func!(bright_blue, Color::BrightBlue, fg);
70 color_func!(bright_magenta, Color::BrightMagenta, fg);
71 color_func!(bright_cyan, Color::BrightCyan, fg);
72 color_func!(bright_white, Color::BrightWhite, fg);
73
74 color_func!(bright_bg_black, Color::BrightBlack, bg);
75 color_func!(bright_bg_red, Color::BrightRed, bg);
76 color_func!(bright_bg_green, Color::BrightGreen, bg);
77 color_func!(bright_bg_yellow, Color::BrightYellow, bg);
78 color_func!(bright_bg_blue, Color::BrightBlue, bg);
79 color_func!(bright_bg_magenta, Color::BrightMagenta, bg);
80 color_func!(bright_bg_cyan, Color::BrightCyan, bg);
81 color_func!(bright_bg_white, Color::BrightWhite, bg);
82
83 color_func!(
84 underline_colored_bright_black,
85 Color::BrightBlack,
86 underline_color
87 );
88 color_func!(
89 underline_colored_bright_red,
90 Color::BrightRed,
91 underline_color
92 );
93 color_func!(
94 underline_colored_bright_green,
95 Color::BrightGreen,
96 underline_color
97 );
98 color_func!(
99 underline_colored_bright_yellow,
100 Color::BrightYellow,
101 underline_color
102 );
103 color_func!(
104 underline_colored_bright_blue,
105 Color::BrightBlue,
106 underline_color
107 );
108 color_func!(
109 underline_colored_bright_magenta,
110 Color::BrightMagenta,
111 underline_color
112 );
113 color_func!(
114 underline_colored_bright_cyan,
115 Color::BrightCyan,
116 underline_color
117 );
118 color_func!(
119 underline_colored_bright_white,
120 Color::BrightWhite,
121 underline_color
122 );
123
124 modifier_func!(not_bold, Modifier::BOLD, without_modifier);
125 modifier_func!(not_dimmed, Modifier::DIM, without_modifier);
126 modifier_func!(not_italic, Modifier::ITALIC, without_modifier);
127 modifier_func!(not_underlined, Modifier::UNDERLINED, without_modifier);
128 modifier_func!(
129 not_double_underlined,
130 Modifier::DOUBLE_UNDERLINED,
131 without_modifier
132 );
133 modifier_func!(
134 not_undercurled,
135 Modifier::UNDERCURLED,
136 without_modifier
137 );
138 modifier_func!(
139 not_underdotted,
140 Modifier::UNDERDOTTED,
141 without_modifier
142 );
143 modifier_func!(
144 not_underfashed,
145 Modifier::UNDERDASHED,
146 without_modifier
147 );
148 modifier_func!(not_slow_blink, Modifier::SLOW_BLINK, without_modifier);
149 modifier_func!(
150 not_rapid_blink,
151 Modifier::RAPID_BLINK,
152 without_modifier
153 );
154 modifier_func!(not_reversed, Modifier::REVERSED, without_modifier);
155 modifier_func!(not_hidden, Modifier::HIDDEN, without_modifier);
156 modifier_func!(
157 not_crossed_out,
158 Modifier::CROSSED_OUT,
159 without_modifier
160 );
161
162 modifier_func!(bold, Modifier::BOLD, with_modifier);
163 modifier_func!(dimmed, Modifier::DIM, with_modifier);
164 modifier_func!(italic, Modifier::ITALIC, with_modifier);
165 modifier_func!(underlined, Modifier::UNDERLINED, with_modifier);
166 modifier_func!(
167 double_underlined,
168 Modifier::DOUBLE_UNDERLINED,
169 with_modifier
170 );
171 modifier_func!(undercurled, Modifier::UNDERCURLED, with_modifier);
172 modifier_func!(underdotted, Modifier::UNDERDOTTED, with_modifier);
173 modifier_func!(underfashed, Modifier::UNDERDASHED, with_modifier);
174 modifier_func!(slow_blink, Modifier::SLOW_BLINK, with_modifier);
175 modifier_func!(rapid_blink, Modifier::RAPID_BLINK, with_modifier);
176 modifier_func!(reversed, Modifier::REVERSED, with_modifier);
177 modifier_func!(hidden, Modifier::HIDDEN, with_modifier);
178 modifier_func!(crossed_out, Modifier::CROSSED_OUT, with_modifier);
179 };
180}
181#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
183#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
184pub struct Style {
185 #[cfg_attr(feature = "serde", serde(default))]
187 pub fg: Option<Color>,
188 #[cfg_attr(feature = "serde", serde(default))]
190 pub bg: Option<Color>,
191 #[cfg_attr(feature = "serde", serde(default))]
193 pub underline_color: Option<Color>,
194 #[cfg_attr(feature = "serde", serde(alias = "modifiers"))]
196 #[cfg_attr(feature = "serde", serde(default))]
197 pub add_modifier: Modifier,
198 #[cfg_attr(feature = "serde", serde(default))]
202 pub sub_modifier: Modifier,
203}
204
205impl Style {
206 #[must_use = "this returns a new modifier"]
208 pub fn overlay(self, overlay: Self) -> Self {
209 let sub = overlay.sub_modifier | self.sub_modifier;
210 Self {
211 fg: overlay.fg.or(self.fg),
212 bg: overlay.bg.or(self.bg),
213 underline_color: overlay.underline_color.or(self.underline_color),
214 add_modifier: {
215 let mut modifier = overlay.add_modifier | self.add_modifier;
216 modifier.remove(sub);
217 modifier
218 },
219 sub_modifier: sub,
220 }
221 }
222
223 #[must_use = "its a builder pattern function"]
225 pub fn fg(mut self, fg: impl Into<Option<Color>>) -> Self {
226 self.fg = fg.into();
227 self
228 }
229
230 #[must_use = "its a builder pattern function"]
232 pub fn bg(mut self, bg: impl Into<Option<Color>>) -> Self {
233 self.bg = bg.into();
234 self
235 }
236
237 #[must_use = "its a builder pattern function"]
239 pub fn underline_color(
240 mut self,
241 underline_color: impl Into<Option<Color>>,
242 ) -> Self {
243 self.underline_color = underline_color.into();
244 self
245 }
246
247 #[must_use = "its a builder pattern function"]
249 pub fn with_modifier(mut self, modifier: Modifier) -> Self {
250 self.add_modifier |= modifier;
251 self.sub_modifier.remove(modifier);
252 self
253 }
254
255 #[must_use = "its a builder pattern function"]
258 pub fn without_modifier(mut self, modifier: Modifier) -> Self {
259 self.sub_modifier |= modifier;
260 self.add_modifier.remove(modifier);
261 self
262 }
263
264 pub fn swapped(mut self) -> Self {
266 mem::swap(&mut self.fg, &mut self.bg);
267 self
268 }
269
270 pub fn realize_color(mut self) -> Self {
272 if self.fg.is_none() {
273 self.fg = Some(Color::Reset);
274 }
275 if self.bg.is_none() {
276 self.bg = Some(Color::Reset);
277 }
278 if self.underline_color.is_none() {
279 self.underline_color = Some(Color::Reset);
280 }
281
282 self
283 }
284
285 funcs!();
286}
287
288#[derive(Clone, Debug, Copy, PartialEq, Eq)]
290#[cfg_attr(
291 feature = "serde",
292 derive(serde_with::DeserializeFromStr, serde_with::SerializeDisplay)
293)]
294pub enum Color {
295 Reset,
297
298 Black,
300 Red,
302 Green,
304 Yellow,
306 Blue,
308 Magenta,
310 Cyan,
312 White,
314
315 BrightBlack,
317 BrightRed,
319 BrightGreen,
321 BrightYellow,
323 BrightBlue,
325 BrightMagenta,
327 BrightCyan,
329 BrightWhite,
331
332 Rgb(u8, u8, u8),
334
335 Indexed(u8),
338}
339
340#[derive(Debug)]
342pub struct UnrecognisedColor(String);
343
344impl Display for UnrecognisedColor {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 f.write_str("unrecognised color: \"")?;
347 f.write_str(&self.0)?;
348 f.write_str("\"")
349 }
350}
351
352impl Display for Color {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 match self {
355 Color::Reset => f.write_str("reset"),
356
357 Color::Black => f.write_str("black"),
358 Color::Red => f.write_str("red"),
359 Color::Green => f.write_str("green"),
360 Color::Yellow => f.write_str("yellow"),
361 Color::Blue => f.write_str("blue"),
362 Color::Magenta => f.write_str("magenta"),
363 Color::Cyan => f.write_str("cyan"),
364 Color::White => f.write_str("white"),
365
366 Color::BrightBlack => f.write_str("bright_black"),
367 Color::BrightRed => f.write_str("bright_red"),
368 Color::BrightGreen => f.write_str("bright_green"),
369 Color::BrightYellow => f.write_str("bright_yellow"),
370 Color::BrightBlue => f.write_str("bright_blue"),
371 Color::BrightMagenta => f.write_str("bright_magenta"),
372 Color::BrightCyan => f.write_str("bright_cyan"),
373 Color::BrightWhite => f.write_str("bright_white"),
374
375 Color::Rgb(r, g, b) => f.write_str(&format!("#{r:x}{g:x}{b:x})")),
376 Color::Indexed(i) => {
377 f.write_str("#")?;
378 <u8 as LowerHex>::fmt(i, f)
379 }
380 }
381 }
382}
383
384impl FromStr for Color {
385 type Err = UnrecognisedColor;
386 fn from_str(s: &str) -> Result<Self, Self::Err> {
387 match s {
388 "reset" => Ok(Color::Reset),
389
390 "black" => Ok(Color::Black),
391 "red" => Ok(Color::Red),
392 "green" => Ok(Color::Green),
393 "yellow" => Ok(Color::Yellow),
394 "blue" => Ok(Color::Blue),
395 "magenta" => Ok(Color::Magenta),
396 "cyan" => Ok(Color::Cyan),
397 "white" => Ok(Color::White),
398
399 "bright_black" => Ok(Color::BrightBlack),
400 "bright_red" => Ok(Color::BrightRed),
401 "bright_green" => Ok(Color::BrightGreen),
402 "bright_yellow" => Ok(Color::BrightYellow),
403 "bright_blue" => Ok(Color::BrightBlue),
404 "bright_magenta" => Ok(Color::BrightMagenta),
405 "bright_cyan" => Ok(Color::BrightCyan),
406 "bright_white" => Ok(Color::BrightWhite),
407
408 s => {
409 if s.starts_with('#') {
411 if s.len() == 7 {
412 let r = &s[1..=2];
414 let g = &s[3..=4];
415 let b = &s[5..=6];
416
417 let r = u8::from_str_radix(r, 16).ok();
419 let g = u8::from_str_radix(g, 16).ok();
420 let b = u8::from_str_radix(b, 16).ok();
421
422 if let Some(((r, g), b)) = r.zip(g).zip(b) {
423 Ok(Color::Rgb(r, g, b))
424 } else {
425 Err(UnrecognisedColor(s.to_string()))
426 }
427 } else if s.len() == 3 {
428 let i = &s[1..=2];
429 match u8::from_str_radix(i, 16) {
430 Ok(i) => Ok(Color::Indexed(i)),
431 Err(_) => Err(UnrecognisedColor(s.to_owned())),
432 }
433 } else {
434 Err(UnrecognisedColor(s.to_string()))
435 }
436 } else {
437 Err(UnrecognisedColor(s.to_string()))
438 }
439 }
440 }
441 }
442}
443
444#[cfg(feature = "image")]
445impl<P> From<P> for Color
446where
447 P: image::Pixel,
448 u8: From<<P as image::Pixel>::Subpixel>,
449{
450 fn from(them: P) -> Self {
451 let them = image::Pixel::to_rgb(&them);
452 Color::Rgb(them[0].into(), them[1].into(), them[2].into())
453 }
454}
455
456impl Color {
457 pub fn from_ansi(ansi: u8) -> Option<Self> {
459 match ansi {
460 0 | 39 | 49 => Some(Self::Reset),
461
462 30 | 40 => Some(Self::Black),
463 31 | 41 => Some(Self::Red),
464 32 | 42 => Some(Self::Green),
465 33 | 43 => Some(Self::Yellow),
466 34 | 44 => Some(Self::Blue),
467 35 | 45 => Some(Self::Magenta),
468 36 | 46 => Some(Self::Cyan),
469 37 | 47 => Some(Self::White),
470
471 90 | 100 => Some(Self::BrightBlack),
472 91 | 101 => Some(Self::BrightRed),
473 92 | 102 => Some(Self::BrightGreen),
474 93 | 103 => Some(Self::BrightYellow),
475 94 | 104 => Some(Self::BrightBlue),
476 95 | 105 => Some(Self::BrightMagenta),
477 96 | 106 => Some(Self::BrightCyan),
478 97 | 107 => Some(Self::BrightWhite),
479
480 _ => None,
481 }
482 }
483}
484
485#[cfg(feature = "crossterm")]
486impl From<Color> for CrossColor {
487 #[cfg(feature = "crossterm")]
488 fn from(them: Color) -> CrossColor {
489 match them {
490 Color::Reset => CrossColor::Reset,
491
492 Color::Black => CrossColor::Black,
493 Color::Red => CrossColor::DarkRed,
494 Color::Green => CrossColor::DarkGreen,
495 Color::Yellow => CrossColor::DarkYellow,
496 Color::Blue => CrossColor::DarkBlue,
497 Color::Magenta => CrossColor::DarkMagenta,
498 Color::Cyan => CrossColor::DarkCyan,
499 Color::White => CrossColor::Grey,
500
501 Color::BrightBlack => CrossColor::DarkGrey,
502 Color::BrightRed => CrossColor::Red,
503 Color::BrightGreen => CrossColor::Green,
504 Color::BrightYellow => CrossColor::Yellow,
505 Color::BrightBlue => CrossColor::Blue,
506 Color::BrightMagenta => CrossColor::Magenta,
507 Color::BrightCyan => CrossColor::Cyan,
508 Color::BrightWhite => CrossColor::White,
509
510 Color::Rgb(r, g, b) => CrossColor::Rgb { r, g, b },
511 Color::Indexed(n) => CrossColor::AnsiValue(n),
512 }
513 }
514}
515
516bitflags::bitflags! {
517 #[derive(Default, Clone, Copy,PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
519 #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(transparent))]
520 pub struct Modifier: u32 {
521 const NONE = 0;
523 const BOLD = 2_u32.pow(1);
525 const DIM = 2_u32.pow(2);
527 const ITALIC = 2_u32.pow(3);
529 const UNDERLINED = 2_u32.pow(4);
531 const SLOW_BLINK = 2_u32.pow(5);
533 const RAPID_BLINK = 2_u32.pow(6);
535 const REVERSED = 2_u32.pow(7);
537 const HIDDEN = 2_u32.pow(8);
539 const CROSSED_OUT = 2_u32.pow(9);
541 const DOUBLE_UNDERLINED = 2_u32.pow(10);
543 const UNDERCURLED = 2_u32.pow(11);
545 const UNDERDOTTED = 2_u32.pow(12);
547 const UNDERDASHED = 2_u32.pow(12);
549 }
550}
551
552#[cfg(feature = "crossterm")]
553impl From<Modifier> for Vec<CrossAttribute> {
554 fn from(them: Modifier) -> Self {
556 let mut vec = Vec::new();
557 if them.contains(Modifier::BOLD) {
558 vec.push(CrossAttribute::Bold);
559 }
560 if them.contains(Modifier::DIM) {
561 vec.push(CrossAttribute::Dim);
562 }
563 if them.contains(Modifier::ITALIC) {
564 vec.push(CrossAttribute::Italic);
565 }
566 if them.contains(Modifier::UNDERLINED) {
567 vec.push(CrossAttribute::Underlined);
568 }
569 if them.contains(Modifier::SLOW_BLINK) {
570 vec.push(CrossAttribute::SlowBlink);
571 }
572 if them.contains(Modifier::RAPID_BLINK) {
573 vec.push(CrossAttribute::RapidBlink);
574 }
575 if them.contains(Modifier::REVERSED) {
576 vec.push(CrossAttribute::Reverse);
577 }
578 if them.contains(Modifier::HIDDEN) {
579 vec.push(CrossAttribute::Hidden);
580 }
581 if them.contains(Modifier::CROSSED_OUT) {
582 vec.push(CrossAttribute::CrossedOut);
583 }
584 if them.contains(Modifier::DOUBLE_UNDERLINED) {
585 vec.push(CrossAttribute::DoubleUnderlined);
586 }
587 if them.contains(Modifier::UNDERCURLED) {
588 vec.push(CrossAttribute::Undercurled);
589 }
590 if them.contains(Modifier::UNDERDOTTED) {
591 vec.push(CrossAttribute::Underdotted);
592 }
593 if them.contains(Modifier::UNDERDASHED) {
594 vec.push(CrossAttribute::Underdashed);
595 }
596 vec
597 }
598}
599
600#[derive(Clone, Default, PartialEq, Eq, Debug)]
602pub struct StyledText<'a> {
603 pub style: Style,
605 pub text: Cow<'a, str>,
607}
608
609impl<'a, T> From<T> for StyledText<'a>
610where
611 Cow<'a, str>: From<T>,
612{
613 fn from(text: T) -> Self {
614 Self {
615 style: Style::default(),
616 text: text.into(),
617 }
618 }
619}
620
621impl StyledText<'_> {
622 #[must_use = "its a builder pattern function"]
624 pub fn style(mut self, style: Style) -> Self {
625 self.style = style;
626 self
627 }
628
629 #[must_use = "its a builder pattern function"]
631 pub fn fg(mut self, fg: impl Into<Option<Color>>) -> Self {
632 self.style.fg = fg.into();
633 self
634 }
635
636 #[must_use = "its a builder pattern function"]
638 pub fn bg(mut self, bg: impl Into<Option<Color>>) -> Self {
639 self.style.bg = bg.into();
640 self
641 }
642
643 #[must_use = "its a builder pattern function"]
645 pub fn underline_color(
646 mut self,
647 underline_color: impl Into<Option<Color>>,
648 ) -> Self {
649 self.style.underline_color = underline_color.into();
650 self
651 }
652
653 #[must_use = "its a builder pattern function"]
655 pub fn with_modifier(mut self, modifier: Modifier) -> Self {
656 self.style.add_modifier |= modifier;
657 self.style.sub_modifier.remove(modifier);
658 self
659 }
660
661 #[must_use = "its a builder pattern function"]
664 pub fn without_modifier(mut self, modifier: Modifier) -> Self {
665 self.style.sub_modifier |= modifier;
666 self.style.add_modifier.remove(modifier);
667 self
668 }
669
670 funcs!();
671}