1#![allow(missing_docs)]
2use crate::style::{Color, Style};
3use crate::write::AnyWrite;
4use std::fmt;
5
6impl Style {
7 fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
9 if self.is_plain() {
13 return Ok(());
14 }
15
16 write!(f, "\x1B[")?;
19 let mut written_anything = false;
20
21 {
22 let mut write_char = |c| {
23 if written_anything {
24 write!(f, ";")?;
25 }
26 written_anything = true;
27 write!(f, "{}", c)?;
28 Ok(())
29 };
30
31 if self.is_bold {
32 write_char('1')?
33 }
34 if self.is_dimmed {
35 write_char('2')?
36 }
37 if self.is_italic {
38 write_char('3')?
39 }
40 if self.is_underline {
41 write_char('4')?
42 }
43 if self.is_blink {
44 write_char('5')?
45 }
46 if self.is_reverse {
47 write_char('7')?
48 }
49 if self.is_hidden {
50 write_char('8')?
51 }
52 if self.is_strikethrough {
53 write_char('9')?
54 }
55 }
56
57 if let Some(bg) = self.background {
61 if written_anything {
62 write!(f, ";")?;
63 }
64 written_anything = true;
65 bg.write_background_code(f)?;
66 }
67
68 if let Some(fg) = self.foreground {
69 if written_anything {
70 write!(f, ";")?;
71 }
72 fg.write_foreground_code(f)?;
73 }
74
75 write!(f, "m")?;
77
78 Ok(())
79 }
80
81 fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
83 if self.is_plain() {
84 Ok(())
85 } else {
86 write!(f, "{}", RESET)
87 }
88 }
89}
90
91pub static RESET: &str = "\x1B[0m";
93
94impl Color {
95 fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
96 match self {
97 Color::Black => write!(f, "30"),
98 Color::Red => write!(f, "31"),
99 Color::Green => write!(f, "32"),
100 Color::Yellow => write!(f, "33"),
101 Color::Blue => write!(f, "34"),
102 Color::Purple => write!(f, "35"),
103 Color::Magenta => write!(f, "35"),
104 Color::Cyan => write!(f, "36"),
105 Color::White => write!(f, "37"),
106 Color::Fixed(num) => write!(f, "38;5;{}", num),
107 Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", r, g, b),
108 Color::Default => write!(f, "39"),
109 Color::DarkGray => write!(f, "90"),
110 Color::LightRed => write!(f, "91"),
111 Color::LightGreen => write!(f, "92"),
112 Color::LightYellow => write!(f, "93"),
113 Color::LightBlue => write!(f, "94"),
114 Color::LightPurple => write!(f, "95"),
115 Color::LightMagenta => write!(f, "95"),
116 Color::LightCyan => write!(f, "96"),
117 Color::LightGray => write!(f, "97"),
118 }
119 }
120
121 fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
122 match self {
123 Color::Black => write!(f, "40"),
124 Color::Red => write!(f, "41"),
125 Color::Green => write!(f, "42"),
126 Color::Yellow => write!(f, "43"),
127 Color::Blue => write!(f, "44"),
128 Color::Purple => write!(f, "45"),
129 Color::Magenta => write!(f, "45"),
130 Color::Cyan => write!(f, "46"),
131 Color::White => write!(f, "47"),
132 Color::Fixed(num) => write!(f, "48;5;{}", num),
133 Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", r, g, b),
134 Color::Default => write!(f, "49"),
135 Color::DarkGray => write!(f, "100"),
136 Color::LightRed => write!(f, "101"),
137 Color::LightGreen => write!(f, "102"),
138 Color::LightYellow => write!(f, "103"),
139 Color::LightBlue => write!(f, "104"),
140 Color::LightPurple => write!(f, "105"),
141 Color::LightMagenta => write!(f, "105"),
142 Color::LightCyan => write!(f, "106"),
143 Color::LightGray => write!(f, "107"),
144 }
145 }
146}
147
148#[derive(Clone, Copy, Debug)]
155pub struct Prefix(Style);
156
157#[derive(Clone, Copy, Debug)]
165pub struct Infix(Style, Style);
166
167#[derive(Clone, Copy, Debug)]
174pub struct Suffix(Style);
175
176impl Style {
177 pub fn prefix(self) -> Prefix {
198 Prefix(self)
199 }
200
201 pub fn infix(self, next: Style) -> Infix {
223 Infix(self, next)
224 }
225
226 pub fn suffix(self) -> Suffix {
247 Suffix(self)
248 }
249}
250
251impl Color {
252 pub fn prefix(self) -> Prefix {
266 Prefix(self.normal())
267 }
268
269 pub fn infix(self, next: Color) -> Infix {
284 Infix(self.normal(), next.normal())
285 }
286
287 pub fn suffix(self) -> Suffix {
301 Suffix(self.normal())
302 }
303}
304
305impl fmt::Display for Prefix {
306 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
307 let f: &mut dyn fmt::Write = f;
308 self.0.write_prefix(f)
309 }
310}
311
312impl fmt::Display for Infix {
313 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
314 use crate::difference::Difference;
315
316 match Difference::between(&self.0, &self.1) {
317 Difference::ExtraStyles(style) => {
318 let f: &mut dyn fmt::Write = f;
319 style.write_prefix(f)
320 }
321 Difference::Reset => {
322 let f: &mut dyn fmt::Write = f;
323 write!(f, "{}{}", RESET, self.1.prefix())
324 }
325 Difference::Empty => {
326 Ok(()) }
328 }
329 }
330}
331
332impl fmt::Display for Suffix {
333 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
334 let f: &mut dyn fmt::Write = f;
335 self.0.write_suffix(f)
336 }
337}
338
339#[cfg(test)]
340mod test {
341 use crate::style::Color::*;
342 use crate::style::Style;
343
344 macro_rules! test {
345 ($name: ident: $style: expr; $input: expr => $result: expr) => {
346 #[test]
347 fn $name() {
348 assert_eq!($style.paint($input).to_string(), $result.to_string());
349
350 let mut v = Vec::new();
351 $style.paint($input.as_bytes()).write_to(&mut v).unwrap();
352 assert_eq!(v.as_slice(), $result.as_bytes());
353 }
354 };
355 }
356
357 test!(plain: Style::default(); "text/plain" => "text/plain");
358 test!(red: Red; "hi" => "\x1B[31mhi\x1B[0m");
359 test!(black: Black.normal(); "hi" => "\x1B[30mhi\x1B[0m");
360 test!(yellow_bold: Yellow.bold(); "hi" => "\x1B[1;33mhi\x1B[0m");
361 test!(yellow_bold_2: Yellow.normal().bold(); "hi" => "\x1B[1;33mhi\x1B[0m");
362 test!(blue_underline: Blue.underline(); "hi" => "\x1B[4;34mhi\x1B[0m");
363 test!(green_bold_ul: Green.bold().underline(); "hi" => "\x1B[1;4;32mhi\x1B[0m");
364 test!(green_bold_ul_2: Green.underline().bold(); "hi" => "\x1B[1;4;32mhi\x1B[0m");
365 test!(purple_on_white: Purple.on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
366 test!(purple_on_white_2: Purple.normal().on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
367 test!(yellow_on_blue: Style::new().on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
368 test!(magenta_on_white: Magenta.on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
369 test!(magenta_on_white_2: Magenta.normal().on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
370 test!(yellow_on_blue_2: Cyan.on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
371 test!(cyan_bold_on_white: Cyan.bold().on(White); "hi" => "\x1B[1;47;36mhi\x1B[0m");
372 test!(cyan_ul_on_white: Cyan.underline().on(White); "hi" => "\x1B[4;47;36mhi\x1B[0m");
373 test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
374 test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
375 test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m");
376 test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
377 test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
378 test!(rgb: Rgb(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
379 test!(rgb_on_blue: Rgb(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
380 test!(blue_on_rgb: Blue.on(Rgb(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
381 test!(rgb_on_rgb: Rgb(70,130,180).on(Rgb(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
382 test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m");
383 test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m");
384 test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m");
385 test!(dimmed: Style::new().dimmed(); "hi" => "\x1B[2mhi\x1B[0m");
386 test!(italic: Style::new().italic(); "hi" => "\x1B[3mhi\x1B[0m");
387 test!(blink: Style::new().blink(); "hi" => "\x1B[5mhi\x1B[0m");
388 test!(reverse: Style::new().reverse(); "hi" => "\x1B[7mhi\x1B[0m");
389 test!(hidden: Style::new().hidden(); "hi" => "\x1B[8mhi\x1B[0m");
390 test!(stricken: Style::new().strikethrough(); "hi" => "\x1B[9mhi\x1B[0m");
391 test!(lr_on_lr: LightRed.on(LightRed); "hi" => "\x1B[101;91mhi\x1B[0m");
392
393 #[test]
394 fn test_infix() {
395 assert_eq!(
396 Style::new().dimmed().infix(Style::new()).to_string(),
397 "\x1B[0m"
398 );
399 assert_eq!(
400 White.dimmed().infix(White.normal()).to_string(),
401 "\x1B[0m\x1B[37m"
402 );
403 assert_eq!(White.normal().infix(White.bold()).to_string(), "\x1B[1m");
404 assert_eq!(White.normal().infix(Blue.normal()).to_string(), "\x1B[34m");
405 assert_eq!(Blue.bold().infix(Blue.bold()).to_string(), "");
406 }
407}