1#![allow(missing_docs)]
2use crate::style::{Color, Style};
3use crate::write::AnyWrite;
4use std::fmt;
5
6impl Style {
7 /// Write any bytes that go *before* a piece of text to the given writer.
8 fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
9 // If there are actually no styles here, then don’t write *any* codes
10 // as the prefix. An empty ANSI code may not affect the terminal
11 // output at all, but a user may just want a code-free string.
12 if self.is_plain() {
13 return Ok(());
14 }
15
16 // Prefix everything with reset characters if needed
17 if self.prefix_with_reset {
18 write!(f, "\x1B[0m")?
19 }
20
21 // Write the codes’ prefix, then write numbers, separated by
22 // semicolons, for each text style we want to apply.
23 write!(f, "\x1B[")?;
24 let mut written_anything = false;
25
26 {
27 let mut write_char = |c| {
28 if written_anything {
29 write!(f, ";")?;
30 }
31 written_anything = true;
32 #[cfg(feature = "gnu_legacy")]
33 write!(f, "0")?;
34 write!(f, "{}", c)?;
35 Ok(())
36 };
37
38 if self.is_bold {
39 write_char('1')?
40 }
41 if self.is_dimmed {
42 write_char('2')?
43 }
44 if self.is_italic {
45 write_char('3')?
46 }
47 if self.is_underline {
48 write_char('4')?
49 }
50 if self.is_blink {
51 write_char('5')?
52 }
53 if self.is_reverse {
54 write_char('7')?
55 }
56 if self.is_hidden {
57 write_char('8')?
58 }
59 if self.is_strikethrough {
60 write_char('9')?
61 }
62 }
63
64 // The foreground and background colors, if specified, need to be
65 // handled specially because the number codes are more complicated.
66 // (see `write_background_code` and `write_foreground_code`)
67
68 #[cfg(feature = "gnu_legacy")]
69 // with GNU, write foreground first, else background first.
70 {
71 if let Some(fg) = self.foreground {
72 if written_anything {
73 write!(f, ";")?;
74 }
75 written_anything = true;
76 fg.write_foreground_code(f)?;
77 }
78
79 if let Some(bg) = self.background {
80 if written_anything {
81 write!(f, ";")?;
82 }
83 bg.write_background_code(f)?;
84 }
85 }
86 #[cfg(not(feature = "gnu_legacy"))]
87 {
88 if let Some(bg) = self.background {
89 if written_anything {
90 write!(f, ";")?;
91 }
92 written_anything = true;
93 bg.write_background_code(f)?;
94 }
95
96 if let Some(fg) = self.foreground {
97 if written_anything {
98 write!(f, ";")?;
99 }
100 fg.write_foreground_code(f)?;
101 }
102 }
103 // All the codes end with an `m`, because reasons.
104 write!(f, "m")?;
105
106 Ok(())
107 }
108
109 /// Write any bytes that go *after* a piece of text to the given writer.
110 fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
111 if self.is_plain() {
112 Ok(())
113 } else {
114 write!(f, "{}", RESET)
115 }
116 }
117}
118
119/// The code to send to reset all styles and return to `Style::default()`.
120pub static RESET: &str = "\x1B[0m";
121
122impl Color {
123 fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
124 match self {
125 Color::Black => write!(f, "30"),
126 Color::Red => write!(f, "31"),
127 Color::Green => write!(f, "32"),
128 Color::Yellow => write!(f, "33"),
129 Color::Blue => write!(f, "34"),
130 Color::Purple => write!(f, "35"),
131 Color::Magenta => write!(f, "35"),
132 Color::Cyan => write!(f, "36"),
133 Color::White => write!(f, "37"),
134 Color::Fixed(num) => write!(f, "38;5;{}", num),
135 Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", r, g, b),
136 Color::Default => write!(f, "39"),
137 Color::DarkGray => write!(f, "90"),
138 Color::LightRed => write!(f, "91"),
139 Color::LightGreen => write!(f, "92"),
140 Color::LightYellow => write!(f, "93"),
141 Color::LightBlue => write!(f, "94"),
142 Color::LightPurple => write!(f, "95"),
143 Color::LightMagenta => write!(f, "95"),
144 Color::LightCyan => write!(f, "96"),
145 Color::LightGray => write!(f, "97"),
146 }
147 }
148
149 fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
150 match self {
151 Color::Black => write!(f, "40"),
152 Color::Red => write!(f, "41"),
153 Color::Green => write!(f, "42"),
154 Color::Yellow => write!(f, "43"),
155 Color::Blue => write!(f, "44"),
156 Color::Purple => write!(f, "45"),
157 Color::Magenta => write!(f, "45"),
158 Color::Cyan => write!(f, "46"),
159 Color::White => write!(f, "47"),
160 Color::Fixed(num) => write!(f, "48;5;{}", num),
161 Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", r, g, b),
162 Color::Default => write!(f, "49"),
163 Color::DarkGray => write!(f, "100"),
164 Color::LightRed => write!(f, "101"),
165 Color::LightGreen => write!(f, "102"),
166 Color::LightYellow => write!(f, "103"),
167 Color::LightBlue => write!(f, "104"),
168 Color::LightPurple => write!(f, "105"),
169 Color::LightMagenta => write!(f, "105"),
170 Color::LightCyan => write!(f, "106"),
171 Color::LightGray => write!(f, "107"),
172 }
173 }
174}
175
176/// Like `AnsiString`, but only displays the style prefix.
177///
178/// This type implements the `Display` trait, meaning it can be written to a
179/// `std::fmt` formatting without doing any extra allocation, and written to a
180/// string with the `.to_string()` method. For examples, see
181/// [`Style::prefix`](struct.Style.html#method.prefix).
182#[derive(Clone, Copy, Debug)]
183pub struct Prefix(Style);
184
185/// Like `AnsiString`, but only displays the difference between two
186/// styles.
187///
188/// This type implements the `Display` trait, meaning it can be written to a
189/// `std::fmt` formatting without doing any extra allocation, and written to a
190/// string with the `.to_string()` method. For examples, see
191/// [`Style::infix`](struct.Style.html#method.infix).
192#[derive(Clone, Copy, Debug)]
193pub struct Infix(Style, Style);
194
195/// Like `AnsiString`, but only displays the style suffix.
196///
197/// This type implements the `Display` trait, meaning it can be written to a
198/// `std::fmt` formatting without doing any extra allocation, and written to a
199/// string with the `.to_string()` method. For examples, see
200/// [`Style::suffix`](struct.Style.html#method.suffix).
201#[derive(Clone, Copy, Debug)]
202pub struct Suffix(Style);
203
204impl Style {
205 /// The prefix bytes for this style. These are the bytes that tell the
206 /// terminal to use a different color or font style.
207 ///
208 /// # Examples
209 ///
210 /// ```
211 /// # #[cfg(not(feature = "gnu_legacy"))]
212 /// # {
213 /// use nu_ansi_term::{Style, Color::Blue};
214 ///
215 /// let style = Style::default().bold();
216 /// assert_eq!("\x1b[1m",
217 /// style.prefix().to_string());
218 ///
219 /// let style = Blue.bold();
220 /// assert_eq!("\x1b[1;34m",
221 /// style.prefix().to_string());
222 ///
223 /// let style = Style::default();
224 /// assert_eq!("",
225 /// style.prefix().to_string());
226 /// # }
227 /// ```
228 ///
229 /// # Examples with gnu_legacy feature enabled
230 /// Styles like bold, underlined, etc. are two-digit now
231 ///
232 /// ```
233 /// # #[cfg(feature = "gnu_legacy")]
234 /// # {
235 /// use nu_ansi_term::{Style, Color::Blue};
236 ///
237 /// let style = Style::default().bold();
238 /// assert_eq!("\x1b[01m",
239 /// style.prefix().to_string());
240 ///
241 /// let style = Blue.bold();
242 /// assert_eq!("\x1b[01;34m",
243 /// style.prefix().to_string());
244 /// # }
245 /// ```
246 pub const fn prefix(self) -> Prefix {
247 Prefix(self)
248 }
249
250 /// The infix bytes between this style and `next` style. These are the bytes
251 /// that tell the terminal to change the style to `next`. These may include
252 /// a reset followed by the next color and style, depending on the two styles.
253 ///
254 /// # Examples
255 /// ```
256 /// # #[cfg(not(feature = "gnu_legacy"))]
257 /// # {
258 /// use nu_ansi_term::{Style, Color::Green};
259 ///
260 /// let style = Style::default().bold();
261 /// assert_eq!("\x1b[32m",
262 /// style.infix(Green.bold()).to_string());
263 ///
264 /// let style = Green.normal();
265 /// assert_eq!("\x1b[1m",
266 /// style.infix(Green.bold()).to_string());
267 ///
268 /// let style = Style::default();
269 /// assert_eq!("",
270 /// style.infix(style).to_string());
271 /// # }
272 /// ```
273 /// # Examples with gnu_legacy feature enabled
274 /// Styles like bold, underlined, etc. are two-digit now
275 /// ```
276 /// # #[cfg(feature = "gnu_legacy")]
277 /// # {
278 /// use nu_ansi_term::Color::Green;
279 ///
280 /// let style = Green.normal();
281 /// assert_eq!("\x1b[01m",
282 /// style.infix(Green.bold()).to_string());
283 /// # }
284 /// ```
285 pub const fn infix(self, next: Style) -> Infix {
286 Infix(self, next)
287 }
288
289 /// The suffix for this style. These are the bytes that tell the terminal
290 /// to reset back to its normal color and font style.
291 ///
292 /// # Examples
293 ///
294 /// ```
295 /// use nu_ansi_term::{Style, Color::Green};
296 ///
297 /// let style = Style::default().bold();
298 /// assert_eq!("\x1b[0m",
299 /// style.suffix().to_string());
300 ///
301 /// let style = Green.normal().bold();
302 /// assert_eq!("\x1b[0m",
303 /// style.suffix().to_string());
304 ///
305 /// let style = Style::default();
306 /// assert_eq!("",
307 /// style.suffix().to_string());
308 /// ```
309 pub const fn suffix(self) -> Suffix {
310 Suffix(self)
311 }
312}
313
314impl Color {
315 /// The prefix bytes for this color as a `Style`. These are the bytes
316 /// that tell the terminal to use a different color or font style.
317 ///
318 /// See also [`Style::prefix`](struct.Style.html#method.prefix).
319 ///
320 /// # Examples
321 ///
322 /// ```
323 /// use nu_ansi_term::Color::Green;
324 ///
325 /// assert_eq!("\x1b[32m",
326 /// Green.prefix().to_string());
327 /// ```
328 pub fn prefix(self) -> Prefix {
329 Prefix(self.normal())
330 }
331
332 /// The infix bytes between this color and `next` color. These are the bytes
333 /// that tell the terminal to use the `next` color, or to do nothing if
334 /// the two colors are equal.
335 ///
336 /// See also [`Style::infix`](struct.Style.html#method.infix).
337 ///
338 /// # Examples
339 ///
340 /// ```
341 /// use nu_ansi_term::Color::{Red, Yellow};
342 ///
343 /// assert_eq!("\x1b[33m",
344 /// Red.infix(Yellow).to_string());
345 /// ```
346 pub fn infix(self, next: Color) -> Infix {
347 Infix(self.normal(), next.normal())
348 }
349
350 /// The suffix for this color as a `Style`. These are the bytes that
351 /// tell the terminal to reset back to its normal color and font style.
352 ///
353 /// See also [`Style::suffix`](struct.Style.html#method.suffix).
354 ///
355 /// # Examples
356 ///
357 /// ```
358 /// use nu_ansi_term::Color::Purple;
359 ///
360 /// assert_eq!("\x1b[0m",
361 /// Purple.suffix().to_string());
362 /// ```
363 pub fn suffix(self) -> Suffix {
364 Suffix(self.normal())
365 }
366}
367
368impl fmt::Display for Prefix {
369 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
370 let f: &mut dyn fmt::Write = f;
371 self.0.write_prefix(f)
372 }
373}
374
375impl fmt::Display for Infix {
376 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
377 use crate::difference::Difference;
378
379 match Difference::between(&self.0, &self.1) {
380 Difference::ExtraStyles(style: Style) => {
381 let f: &mut dyn fmt::Write = f;
382 style.write_prefix(f)
383 }
384 Difference::Reset => {
385 let f: &mut dyn fmt::Write = f;
386 write!(f, "{}{}", RESET, self.1.prefix())
387 }
388 Difference::Empty => {
389 Ok(()) // nothing to write
390 }
391 }
392 }
393}
394
395impl fmt::Display for Suffix {
396 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
397 let f: &mut dyn fmt::Write = f;
398 self.0.write_suffix(f)
399 }
400}
401
402#[cfg(test)]
403macro_rules! test {
404 ($name: ident: $style: expr; $input: expr => $result: expr) => {
405 #[test]
406 fn $name() {
407 assert_eq!($style.paint($input).to_string(), $result.to_string());
408
409 let mut v = Vec::new();
410 $style.paint($input.as_bytes()).write_to(&mut v).unwrap();
411 assert_eq!(v.as_slice(), $result.as_bytes());
412 }
413 };
414}
415
416#[cfg(test)]
417#[cfg(not(feature = "gnu_legacy"))]
418mod test {
419 use crate::style::Color::*;
420 use crate::style::Style;
421 use crate::Color;
422 use std::default::Default;
423
424 test!(plain: Style::default(); "text/plain" => "text/plain");
425 test!(red: Red; "hi" => "\x1B[31mhi\x1B[0m");
426 test!(black: Black.normal(); "hi" => "\x1B[30mhi\x1B[0m");
427 test!(yellow_bold: Yellow.bold(); "hi" => "\x1B[1;33mhi\x1B[0m");
428 test!(yellow_bold_2: Yellow.normal().bold(); "hi" => "\x1B[1;33mhi\x1B[0m");
429 test!(blue_underline: Blue.underline(); "hi" => "\x1B[4;34mhi\x1B[0m");
430 test!(green_bold_ul: Green.bold().underline(); "hi" => "\x1B[1;4;32mhi\x1B[0m");
431 test!(green_bold_ul_2: Green.underline().bold(); "hi" => "\x1B[1;4;32mhi\x1B[0m");
432 test!(purple_on_white: Purple.on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
433 test!(purple_on_white_2: Purple.normal().on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
434 test!(yellow_on_blue: Style::new().on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
435 test!(magenta_on_white: Magenta.on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
436 test!(magenta_on_white_2: Magenta.normal().on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
437 test!(yellow_on_blue_2: Cyan.on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
438 test!(yellow_on_blue_reset: Cyan.on(Blue).reset_before_style().fg(Yellow); "hi" => "\x1B[0m\x1B[44;33mhi\x1B[0m");
439 test!(yellow_on_blue_reset_2: Cyan.on(Blue).fg(Yellow).reset_before_style(); "hi" => "\x1B[0m\x1B[44;33mhi\x1B[0m");
440 test!(cyan_bold_on_white: Cyan.bold().on(White); "hi" => "\x1B[1;47;36mhi\x1B[0m");
441 test!(cyan_ul_on_white: Cyan.underline().on(White); "hi" => "\x1B[4;47;36mhi\x1B[0m");
442 test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
443 test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
444 test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m");
445 test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
446 test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
447 test!(rgb: Rgb(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
448 test!(rgb_on_blue: Rgb(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
449 test!(blue_on_rgb: Blue.on(Rgb(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
450 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");
451 test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m");
452 test!(bold_with_reset: Style::new().reset_before_style().bold(); "hi" => "\x1B[0m\x1B[1mhi\x1B[0m");
453 test!(bold_with_reset_2: Style::new().bold().reset_before_style(); "hi" => "\x1B[0m\x1B[1mhi\x1B[0m");
454 test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m");
455 test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m");
456 test!(dimmed: Style::new().dimmed(); "hi" => "\x1B[2mhi\x1B[0m");
457 test!(italic: Style::new().italic(); "hi" => "\x1B[3mhi\x1B[0m");
458 test!(blink: Style::new().blink(); "hi" => "\x1B[5mhi\x1B[0m");
459 test!(reverse: Style::new().reverse(); "hi" => "\x1B[7mhi\x1B[0m");
460 test!(hidden: Style::new().hidden(); "hi" => "\x1B[8mhi\x1B[0m");
461 test!(stricken: Style::new().strikethrough(); "hi" => "\x1B[9mhi\x1B[0m");
462 test!(lr_on_lr: LightRed.on(LightRed); "hi" => "\x1B[101;91mhi\x1B[0m");
463
464 #[test]
465 fn test_infix() {
466 assert_eq!(
467 Style::new().dimmed().infix(Style::new()).to_string(),
468 "\x1B[0m"
469 );
470 assert_eq!(
471 White.dimmed().infix(White.normal()).to_string(),
472 "\x1B[0m\x1B[37m"
473 );
474 assert_eq!(White.normal().infix(White.bold()).to_string(), "\x1B[1m");
475 assert_eq!(White.normal().infix(Blue.normal()).to_string(), "\x1B[34m");
476 assert_eq!(Blue.bold().infix(Blue.bold()).to_string(), "");
477 }
478
479 #[test]
480 fn test_write_prefix_no_gnu_compat_order() {
481 let style = Style {
482 foreground: Some(Color::Red),
483 background: Some(Color::Blue),
484 ..Default::default()
485 };
486 assert_eq!(
487 style.paint("file").to_string(),
488 "\u{1b}[44;31mfile\u{1b}[0m".to_string()
489 );
490 }
491}
492
493#[cfg(test)]
494#[cfg(feature = "gnu_legacy")]
495mod gnu_legacy_test {
496 use crate::style::Color::*;
497 use crate::style::Style;
498 use crate::Color;
499 use std::default::Default;
500
501 test!(plain: Style::default(); "text/plain" => "text/plain");
502 test!(red: Red; "hi" => "\x1B[31mhi\x1B[0m");
503 test!(black: Black.normal(); "hi" => "\x1B[30mhi\x1B[0m");
504 test!(yellow_bold: Yellow.bold(); "hi" => "\x1B[01;33mhi\x1B[0m");
505 test!(yellow_bold_2: Yellow.normal().bold(); "hi" => "\x1B[01;33mhi\x1B[0m");
506 test!(blue_underline: Blue.underline(); "hi" => "\x1B[04;34mhi\x1B[0m");
507 test!(green_bold_ul: Green.bold().underline(); "hi" => "\x1B[01;04;32mhi\x1B[0m");
508 test!(green_bold_ul_2: Green.underline().bold(); "hi" => "\x1B[01;04;32mhi\x1B[0m");
509 test!(purple_on_white: Purple.on(White); "hi" => "\x1B[35;47mhi\x1B[0m");
510 test!(purple_on_white_2: Purple.normal().on(White); "hi" => "\x1B[35;47mhi\x1B[0m");
511 test!(yellow_on_blue: Style::new().on(Blue).fg(Yellow); "hi" => "\x1B[33;44mhi\x1B[0m");
512 test!(yellow_on_blue_reset_2: Cyan.on(Blue).fg(Yellow).reset_before_style(); "hi" => "\x1B[0m\x1B[33;44mhi\x1B[0m");
513 test!(magenta_on_white: Magenta.on(White); "hi" => "\x1B[35;47mhi\x1B[0m");
514 test!(magenta_on_white_2: Magenta.normal().on(White); "hi" => "\x1B[35;47mhi\x1B[0m");
515 test!(yellow_on_blue_2: Cyan.on(Blue).fg(Yellow); "hi" => "\x1B[33;44mhi\x1B[0m");
516 test!(cyan_bold_on_white: Cyan.bold().on(White); "hi" => "\x1B[01;36;47mhi\x1B[0m");
517 test!(cyan_ul_on_white: Cyan.underline().on(White); "hi" => "\x1B[04;36;47mhi\x1B[0m");
518 test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[01;04;36;47mhi\x1B[0m");
519 test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[01;04;36;47mhi\x1B[0m");
520 test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m");
521 test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[38;5;100;45mhi\x1B[0m");
522 test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[38;5;100;48;5;200mhi\x1B[0m");
523 test!(rgb: Rgb(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
524 test!(rgb_on_blue: Rgb(70,130,180).on(Blue); "hi" => "\x1B[38;2;70;130;180;44mhi\x1B[0m");
525 test!(blue_on_rgb: Blue.on(Rgb(70,130,180)); "hi" => "\x1B[34;48;2;70;130;180mhi\x1B[0m");
526 test!(rgb_on_rgb: Rgb(70,130,180).on(Rgb(5,10,15)); "hi" => "\x1B[38;2;70;130;180;48;2;5;10;15mhi\x1B[0m");
527 test!(bold: Style::new().bold(); "hi" => "\x1B[01mhi\x1B[0m");
528 test!(bold_with_reset: Style::new().reset_before_style().bold(); "hi" => "\x1B[0m\x1B[01mhi\x1B[0m");
529 test!(bold_with_reset_2: Style::new().bold().reset_before_style(); "hi" => "\x1B[0m\x1B[01mhi\x1B[0m");
530 test!(underline: Style::new().underline(); "hi" => "\x1B[04mhi\x1B[0m");
531 test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[01;04mhi\x1B[0m");
532 test!(dimmed: Style::new().dimmed(); "hi" => "\x1B[02mhi\x1B[0m");
533 test!(italic: Style::new().italic(); "hi" => "\x1B[03mhi\x1B[0m");
534 test!(blink: Style::new().blink(); "hi" => "\x1B[05mhi\x1B[0m");
535 test!(reverse: Style::new().reverse(); "hi" => "\x1B[07mhi\x1B[0m");
536 test!(hidden: Style::new().hidden(); "hi" => "\x1B[08mhi\x1B[0m");
537 test!(stricken: Style::new().strikethrough(); "hi" => "\x1B[09mhi\x1B[0m");
538 test!(lr_on_lr: LightRed.on(LightRed); "hi" => "\x1B[91;101mhi\x1B[0m");
539
540 #[test]
541 fn test_write_prefix_gnu_compat_order() {
542 let style = Style {
543 foreground: Some(Color::Red),
544 background: Some(Color::Blue),
545 ..Default::default()
546 };
547 assert_eq!(
548 style.paint("file").to_string(),
549 "\u{1b}[31;44mfile\u{1b}[0m".to_string()
550 );
551 }
552}
553