1//!Coloring terminal so simple, you already know how to do it !
2//!
3//! use colored::Colorize;
4//!
5//! "this is blue".blue();
6//! "this is red".red();
7//! "this is red on blue".red().on_blue();
8//! "this is also red on blue".on_blue().red();
9//! "you can use truecolor values too!".truecolor(0, 255, 136);
10//! "background truecolor also works :)".on_truecolor(135, 28, 167);
11//! "you can also make bold comments".bold();
12//! println!("{} {} {}", "or use".cyan(), "any".italic().yellow(), "string type".cyan());
13//! "or change advice. This is red".yellow().blue().red();
14//! "or clear things up. This is default color and style".red().bold().clear();
15//! "purple and magenta are the same".purple().magenta();
16//! "bright colors are also allowed".bright_blue().on_bright_white();
17//! "you can specify color by string".color("blue").on_color("red");
18//! "and so are normal and clear".normal().clear();
19//! String::from("this also works!").green().bold();
20//! format!("{:30}", "format works as expected. This will be padded".blue());
21//! format!("{:.3}", "and this will be green but truncated to 3 chars".green());
22//!
23//!
24//! See [the `Colorize` trait](./trait.Colorize.html) for all the methods.
25//!
26#![warn(missing_docs)]
27
28extern crate is_terminal;
29#[macro_use]
30extern crate lazy_static;
31
32#[cfg(test)]
33extern crate rspec;
34
35mod color;
36pub mod control;
37mod style;
38
39pub use self::customcolors::CustomColor;
40
41/// Custom colors support.
42pub mod customcolors;
43
44pub use color::*;
45
46use std::{borrow::Cow, fmt, ops::Deref};
47
48pub use style::{Style, Styles};
49
50/// A string that may have color and/or style applied to it.
51#[derive(Clone, Debug, PartialEq, Eq)]
52pub struct ColoredString {
53 input: String,
54 fgcolor: Option<Color>,
55 bgcolor: Option<Color>,
56 style: style::Style,
57}
58
59/// The trait that enables something to be given color.
60///
61/// You can use `colored` effectively simply by importing this trait
62/// and then using its methods on `String` and `&str`.
63#[allow(missing_docs)]
64pub trait Colorize {
65 // Font Colors
66 fn black(self) -> ColoredString
67 where
68 Self: Sized,
69 {
70 self.color(Color::Black)
71 }
72 fn red(self) -> ColoredString
73 where
74 Self: Sized,
75 {
76 self.color(Color::Red)
77 }
78 fn green(self) -> ColoredString
79 where
80 Self: Sized,
81 {
82 self.color(Color::Green)
83 }
84 fn yellow(self) -> ColoredString
85 where
86 Self: Sized,
87 {
88 self.color(Color::Yellow)
89 }
90 fn blue(self) -> ColoredString
91 where
92 Self: Sized,
93 {
94 self.color(Color::Blue)
95 }
96 fn magenta(self) -> ColoredString
97 where
98 Self: Sized,
99 {
100 self.color(Color::Magenta)
101 }
102 fn purple(self) -> ColoredString
103 where
104 Self: Sized,
105 {
106 self.color(Color::Magenta)
107 }
108 fn cyan(self) -> ColoredString
109 where
110 Self: Sized,
111 {
112 self.color(Color::Cyan)
113 }
114 fn white(self) -> ColoredString
115 where
116 Self: Sized,
117 {
118 self.color(Color::White)
119 }
120 fn bright_black(self) -> ColoredString
121 where
122 Self: Sized,
123 {
124 self.color(Color::BrightBlack)
125 }
126 fn bright_red(self) -> ColoredString
127 where
128 Self: Sized,
129 {
130 self.color(Color::BrightRed)
131 }
132 fn bright_green(self) -> ColoredString
133 where
134 Self: Sized,
135 {
136 self.color(Color::BrightGreen)
137 }
138 fn bright_yellow(self) -> ColoredString
139 where
140 Self: Sized,
141 {
142 self.color(Color::BrightYellow)
143 }
144 fn bright_blue(self) -> ColoredString
145 where
146 Self: Sized,
147 {
148 self.color(Color::BrightBlue)
149 }
150 fn bright_magenta(self) -> ColoredString
151 where
152 Self: Sized,
153 {
154 self.color(Color::BrightMagenta)
155 }
156 fn bright_purple(self) -> ColoredString
157 where
158 Self: Sized,
159 {
160 self.color(Color::BrightMagenta)
161 }
162 fn bright_cyan(self) -> ColoredString
163 where
164 Self: Sized,
165 {
166 self.color(Color::BrightCyan)
167 }
168 fn bright_white(self) -> ColoredString
169 where
170 Self: Sized,
171 {
172 self.color(Color::BrightWhite)
173 }
174 fn truecolor(self, r: u8, g: u8, b: u8) -> ColoredString
175 where
176 Self: Sized,
177 {
178 self.color(Color::TrueColor { r, g, b })
179 }
180 fn custom_color(self, color: CustomColor) -> ColoredString
181 where
182 Self: Sized,
183 {
184 self.color(Color::TrueColor {
185 r: color.r,
186 g: color.g,
187 b: color.b,
188 })
189 }
190 fn color<S: Into<Color>>(self, color: S) -> ColoredString;
191 // Background Colors
192 fn on_black(self) -> ColoredString
193 where
194 Self: Sized,
195 {
196 self.on_color(Color::Black)
197 }
198 fn on_red(self) -> ColoredString
199 where
200 Self: Sized,
201 {
202 self.on_color(Color::Red)
203 }
204 fn on_green(self) -> ColoredString
205 where
206 Self: Sized,
207 {
208 self.on_color(Color::Green)
209 }
210 fn on_yellow(self) -> ColoredString
211 where
212 Self: Sized,
213 {
214 self.on_color(Color::Yellow)
215 }
216 fn on_blue(self) -> ColoredString
217 where
218 Self: Sized,
219 {
220 self.on_color(Color::Blue)
221 }
222 fn on_magenta(self) -> ColoredString
223 where
224 Self: Sized,
225 {
226 self.on_color(Color::Magenta)
227 }
228 fn on_purple(self) -> ColoredString
229 where
230 Self: Sized,
231 {
232 self.on_color(Color::Magenta)
233 }
234 fn on_cyan(self) -> ColoredString
235 where
236 Self: Sized,
237 {
238 self.on_color(Color::Cyan)
239 }
240 fn on_white(self) -> ColoredString
241 where
242 Self: Sized,
243 {
244 self.on_color(Color::White)
245 }
246 fn on_bright_black(self) -> ColoredString
247 where
248 Self: Sized,
249 {
250 self.on_color(Color::BrightBlack)
251 }
252 fn on_bright_red(self) -> ColoredString
253 where
254 Self: Sized,
255 {
256 self.on_color(Color::BrightRed)
257 }
258 fn on_bright_green(self) -> ColoredString
259 where
260 Self: Sized,
261 {
262 self.on_color(Color::BrightGreen)
263 }
264 fn on_bright_yellow(self) -> ColoredString
265 where
266 Self: Sized,
267 {
268 self.on_color(Color::BrightYellow)
269 }
270 fn on_bright_blue(self) -> ColoredString
271 where
272 Self: Sized,
273 {
274 self.on_color(Color::BrightBlue)
275 }
276 fn on_bright_magenta(self) -> ColoredString
277 where
278 Self: Sized,
279 {
280 self.on_color(Color::BrightMagenta)
281 }
282 fn on_bright_purple(self) -> ColoredString
283 where
284 Self: Sized,
285 {
286 self.on_color(Color::BrightMagenta)
287 }
288 fn on_bright_cyan(self) -> ColoredString
289 where
290 Self: Sized,
291 {
292 self.on_color(Color::BrightCyan)
293 }
294 fn on_bright_white(self) -> ColoredString
295 where
296 Self: Sized,
297 {
298 self.on_color(Color::BrightWhite)
299 }
300 fn on_truecolor(self, r: u8, g: u8, b: u8) -> ColoredString
301 where
302 Self: Sized,
303 {
304 self.on_color(Color::TrueColor { r, g, b })
305 }
306 fn on_custom_color(self, color: CustomColor) -> ColoredString
307 where
308 Self: Sized,
309 {
310 self.on_color(Color::TrueColor {
311 r: color.r,
312 g: color.g,
313 b: color.b,
314 })
315 }
316 fn on_color<S: Into<Color>>(self, color: S) -> ColoredString;
317 // Styles
318 fn clear(self) -> ColoredString;
319 fn normal(self) -> ColoredString;
320 fn bold(self) -> ColoredString;
321 fn dimmed(self) -> ColoredString;
322 fn italic(self) -> ColoredString;
323 fn underline(self) -> ColoredString;
324 fn blink(self) -> ColoredString;
325 #[deprecated(since = "1.5.2", note = "Users should use reversed instead")]
326 fn reverse(self) -> ColoredString;
327 fn reversed(self) -> ColoredString;
328 fn hidden(self) -> ColoredString;
329 fn strikethrough(self) -> ColoredString;
330}
331
332impl ColoredString {
333 /// Get the current background color applied.
334 ///
335 /// ```rust
336 /// # use colored::*;
337 /// let cstr = "".blue();
338 /// assert_eq!(cstr.fgcolor(), Some(Color::Blue));
339 /// let cstr = cstr.clear();
340 /// assert_eq!(cstr.fgcolor(), None);
341 /// ```
342 pub fn fgcolor(&self) -> Option<Color> {
343 self.fgcolor.as_ref().copied()
344 }
345
346 /// Get the current background color applied.
347 ///
348 /// ```rust
349 /// # use colored::*;
350 /// let cstr = "".on_blue();
351 /// assert_eq!(cstr.bgcolor(), Some(Color::Blue));
352 /// let cstr = cstr.clear();
353 /// assert_eq!(cstr.bgcolor(), None);
354 /// ```
355 pub fn bgcolor(&self) -> Option<Color> {
356 self.bgcolor.as_ref().copied()
357 }
358
359 /// Get the current [`Style`] which can be check if it contains a [`Styles`].
360 ///
361 /// ```rust
362 /// # use colored::*;
363 /// let colored = "".bold().italic();
364 /// assert_eq!(colored.style().contains(Styles::Bold), true);
365 /// assert_eq!(colored.style().contains(Styles::Italic), true);
366 /// assert_eq!(colored.style().contains(Styles::Dimmed), false);
367 /// ```
368 pub fn style(&self) -> style::Style {
369 self.style
370 }
371
372 /// Checks if the colored string has no color or styling.
373 ///
374 /// ```rust
375 /// # use colored::*;
376 /// let cstr = "".red();
377 /// assert_eq!(cstr.is_plain(), false);
378 /// let cstr = cstr.clear();
379 /// assert_eq!(cstr.is_plain(), true);
380 /// ```
381 pub fn is_plain(&self) -> bool {
382 self.bgcolor.is_none() && self.fgcolor.is_none() && self.style == style::CLEAR
383 }
384
385 #[cfg(not(feature = "no-color"))]
386 fn has_colors(&self) -> bool {
387 control::SHOULD_COLORIZE.should_colorize()
388 }
389
390 #[cfg(feature = "no-color")]
391 fn has_colors(&self) -> bool {
392 false
393 }
394
395 fn compute_style(&self) -> String {
396 if !self.has_colors() || self.is_plain() {
397 return String::new();
398 }
399
400 let mut res = String::from("\x1B[");
401 let mut has_wrote = if self.style != style::CLEAR {
402 res.push_str(&self.style.to_str());
403 true
404 } else {
405 false
406 };
407
408 if let Some(ref bgcolor) = self.bgcolor {
409 if has_wrote {
410 res.push(';');
411 }
412
413 res.push_str(&bgcolor.to_bg_str());
414 has_wrote = true;
415 }
416
417 if let Some(ref fgcolor) = self.fgcolor {
418 if has_wrote {
419 res.push(';');
420 }
421
422 res.push_str(&fgcolor.to_fg_str());
423 }
424
425 res.push('m');
426 res
427 }
428
429 fn escape_inner_reset_sequences(&self) -> Cow<str> {
430 if !self.has_colors() || self.is_plain() {
431 return self.input.as_str().into();
432 }
433
434 // TODO: BoyScoutRule
435 let reset = "\x1B[0m";
436 let style = self.compute_style();
437 let matches: Vec<usize> = self
438 .input
439 .match_indices(reset)
440 .map(|(idx, _)| idx)
441 .collect();
442 if matches.is_empty() {
443 return self.input.as_str().into();
444 }
445
446 let mut input = self.input.clone();
447 input.reserve(matches.len() * style.len());
448
449 for (idx_in_matches, offset) in matches.into_iter().enumerate() {
450 // shift the offset to the end of the reset sequence and take in account
451 // the number of matches we have escaped (which shift the index to insert)
452 let mut offset = offset + reset.len() + idx_in_matches * style.len();
453
454 for cchar in style.chars() {
455 input.insert(offset, cchar);
456 offset += 1;
457 }
458 }
459
460 input.into()
461 }
462}
463
464impl Default for ColoredString {
465 fn default() -> Self {
466 ColoredString {
467 input: String::default(),
468 fgcolor: None,
469 bgcolor: None,
470 style: style::CLEAR,
471 }
472 }
473}
474
475impl Deref for ColoredString {
476 type Target = str;
477 fn deref(&self) -> &str {
478 &self.input
479 }
480}
481
482impl<'a> From<&'a str> for ColoredString {
483 fn from(s: &'a str) -> Self {
484 ColoredString {
485 input: String::from(s),
486 ..ColoredString::default()
487 }
488 }
489}
490
491impl Colorize for ColoredString {
492 fn color<S: Into<Color>>(mut self, color: S) -> ColoredString {
493 self.fgcolor = Some(color.into());
494 self
495 }
496 fn on_color<S: Into<Color>>(mut self, color: S) -> ColoredString {
497 self.bgcolor = Some(color.into());
498 self
499 }
500
501 fn clear(self) -> ColoredString {
502 ColoredString {
503 input: self.input,
504 ..ColoredString::default()
505 }
506 }
507 fn normal(self) -> ColoredString {
508 self.clear()
509 }
510 fn bold(mut self) -> ColoredString {
511 self.style.add(style::Styles::Bold);
512 self
513 }
514 fn dimmed(mut self) -> ColoredString {
515 self.style.add(style::Styles::Dimmed);
516 self
517 }
518 fn italic(mut self) -> ColoredString {
519 self.style.add(style::Styles::Italic);
520 self
521 }
522 fn underline(mut self) -> ColoredString {
523 self.style.add(style::Styles::Underline);
524 self
525 }
526 fn blink(mut self) -> ColoredString {
527 self.style.add(style::Styles::Blink);
528 self
529 }
530 fn reverse(self) -> ColoredString {
531 self.reversed()
532 }
533 fn reversed(mut self) -> ColoredString {
534 self.style.add(style::Styles::Reversed);
535 self
536 }
537 fn hidden(mut self) -> ColoredString {
538 self.style.add(style::Styles::Hidden);
539 self
540 }
541 fn strikethrough(mut self) -> ColoredString {
542 self.style.add(style::Styles::Strikethrough);
543 self
544 }
545}
546
547impl<'a> Colorize for &'a str {
548 fn color<S: Into<Color>>(self, color: S) -> ColoredString {
549 ColoredString {
550 fgcolor: Some(color.into()),
551 input: String::from(self),
552 ..ColoredString::default()
553 }
554 }
555
556 fn on_color<S: Into<Color>>(self, color: S) -> ColoredString {
557 ColoredString {
558 bgcolor: Some(color.into()),
559 input: String::from(self),
560 ..ColoredString::default()
561 }
562 }
563
564 fn clear(self) -> ColoredString {
565 ColoredString {
566 input: String::from(self),
567 style: style::CLEAR,
568 ..ColoredString::default()
569 }
570 }
571 fn normal(self) -> ColoredString {
572 self.clear()
573 }
574 fn bold(self) -> ColoredString {
575 ColoredString::from(self).bold()
576 }
577 fn dimmed(self) -> ColoredString {
578 ColoredString::from(self).dimmed()
579 }
580 fn italic(self) -> ColoredString {
581 ColoredString::from(self).italic()
582 }
583 fn underline(self) -> ColoredString {
584 ColoredString::from(self).underline()
585 }
586 fn blink(self) -> ColoredString {
587 ColoredString::from(self).blink()
588 }
589 fn reverse(self) -> ColoredString {
590 self.reversed()
591 }
592 fn reversed(self) -> ColoredString {
593 ColoredString::from(self).reversed()
594 }
595 fn hidden(self) -> ColoredString {
596 ColoredString::from(self).hidden()
597 }
598 fn strikethrough(self) -> ColoredString {
599 ColoredString::from(self).strikethrough()
600 }
601}
602
603impl fmt::Display for ColoredString {
604 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
605 if !self.has_colors() || self.is_plain() {
606 return <String as fmt::Display>::fmt(&self.input, f);
607 }
608
609 // XXX: see tests. Useful when nesting colored strings
610 let escaped_input: Cow<'_, str> = self.escape_inner_reset_sequences();
611
612 f.write_str(&self.compute_style())?;
613 escaped_input.fmt(f)?;
614 f.write_str(data:"\x1B[0m")?;
615 Ok(())
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622
623 #[test]
624 fn formatting() {
625 // respect the formatting. Escape sequence add some padding so >= 40
626 assert!(format!("{:40}", "".blue()).len() >= 40);
627 // both should be truncated to 1 char before coloring
628 assert_eq!(
629 format!("{:1.1}", "toto".blue()).len(),
630 format!("{:1.1}", "1".blue()).len()
631 )
632 }
633
634 #[test]
635 fn it_works() {
636 let toto = "toto";
637 println!("{}", toto.red());
638 println!("{}", String::from(toto).red());
639 println!("{}", toto.blue());
640
641 println!("blue style ****");
642 println!("{}", toto.bold());
643 println!("{}", "yeah ! Red bold !".red().bold());
644 println!("{}", "yeah ! Yellow bold !".bold().yellow());
645 println!("{}", toto.bold().blue());
646 println!("{}", toto.blue().bold());
647 println!("{}", toto.blue().bold().underline());
648 println!("{}", toto.blue().italic());
649 println!("******");
650 println!("test clearing");
651 println!("{}", "red cleared".red().clear());
652 println!("{}", "bold cyan cleared".bold().cyan().clear());
653 println!("******");
654 println!("Bg tests");
655 println!("{}", toto.green().on_blue());
656 println!("{}", toto.on_magenta().yellow());
657 println!("{}", toto.purple().on_yellow());
658 println!("{}", toto.magenta().on_white());
659 println!("{}", toto.cyan().on_green());
660 println!("{}", toto.black().on_white());
661 println!("******");
662 println!("{}", toto.green());
663 println!("{}", toto.yellow());
664 println!("{}", toto.purple());
665 println!("{}", toto.magenta());
666 println!("{}", toto.cyan());
667 println!("{}", toto.white());
668 println!("{}", toto.white().red().blue().green());
669 println!("{}", toto.truecolor(255, 0, 0));
670 println!("{}", toto.truecolor(255, 255, 0));
671 println!("{}", toto.on_truecolor(0, 80, 80));
672 // uncomment to see term output
673 // assert!(false)
674 }
675
676 #[test]
677 fn compute_style_empty_string() {
678 assert_eq!("", "".clear().compute_style());
679 }
680
681 #[cfg_attr(feature = "no-color", ignore)]
682 #[test]
683 fn compute_style_simple_fg_blue() {
684 let blue = "\x1B[34m";
685
686 assert_eq!(blue, "".blue().compute_style());
687 }
688
689 #[cfg_attr(feature = "no-color", ignore)]
690 #[test]
691 fn compute_style_simple_bg_blue() {
692 let on_blue = "\x1B[44m";
693
694 assert_eq!(on_blue, "".on_blue().compute_style());
695 }
696
697 #[cfg_attr(feature = "no-color", ignore)]
698 #[test]
699 fn compute_style_blue_on_blue() {
700 let blue_on_blue = "\x1B[44;34m";
701
702 assert_eq!(blue_on_blue, "".blue().on_blue().compute_style());
703 }
704
705 #[cfg_attr(feature = "no-color", ignore)]
706 #[test]
707 fn compute_style_simple_fg_bright_blue() {
708 let blue = "\x1B[94m";
709
710 assert_eq!(blue, "".bright_blue().compute_style());
711 }
712
713 #[cfg_attr(feature = "no-color", ignore)]
714 #[test]
715 fn compute_style_simple_bg_bright_blue() {
716 let on_blue = "\x1B[104m";
717
718 assert_eq!(on_blue, "".on_bright_blue().compute_style());
719 }
720
721 #[cfg_attr(feature = "no-color", ignore)]
722 #[test]
723 fn compute_style_bright_blue_on_bright_blue() {
724 let blue_on_blue = "\x1B[104;94m";
725
726 assert_eq!(
727 blue_on_blue,
728 "".bright_blue().on_bright_blue().compute_style()
729 );
730 }
731
732 #[cfg_attr(feature = "no-color", ignore)]
733 #[test]
734 fn compute_style_simple_bold() {
735 let bold = "\x1B[1m";
736
737 assert_eq!(bold, "".bold().compute_style());
738 }
739
740 #[cfg_attr(feature = "no-color", ignore)]
741 #[test]
742 fn compute_style_blue_bold() {
743 let blue_bold = "\x1B[1;34m";
744
745 assert_eq!(blue_bold, "".blue().bold().compute_style());
746 }
747
748 #[cfg_attr(feature = "no-color", ignore)]
749 #[test]
750 fn compute_style_blue_bold_on_blue() {
751 let blue_bold_on_blue = "\x1B[1;44;34m";
752
753 assert_eq!(
754 blue_bold_on_blue,
755 "".blue().bold().on_blue().compute_style()
756 );
757 }
758
759 #[test]
760 fn escape_reset_sequence_spec_should_do_nothing_on_empty_strings() {
761 let style = ColoredString::default();
762 let expected = String::new();
763
764 let output = style.escape_inner_reset_sequences();
765
766 assert_eq!(expected, output);
767 }
768
769 #[test]
770 fn escape_reset_sequence_spec_should_do_nothing_on_string_with_no_reset() {
771 let style = ColoredString {
772 input: String::from("hello world !"),
773 ..ColoredString::default()
774 };
775
776 let expected = String::from("hello world !");
777 let output = style.escape_inner_reset_sequences();
778
779 assert_eq!(expected, output);
780 }
781
782 #[cfg_attr(feature = "no-color", ignore)]
783 #[test]
784 fn escape_reset_sequence_spec_should_replace_inner_reset_sequence_with_current_style() {
785 let input = format!("start {} end", String::from("hello world !").red());
786 let style = input.blue();
787
788 let output = style.escape_inner_reset_sequences();
789 let blue = "\x1B[34m";
790 let red = "\x1B[31m";
791 let reset = "\x1B[0m";
792 let expected = format!("start {}hello world !{}{} end", red, reset, blue);
793 assert_eq!(expected, output);
794 }
795
796 #[cfg_attr(feature = "no-color", ignore)]
797 #[test]
798 fn escape_reset_sequence_spec_should_replace_multiple_inner_reset_sequences_with_current_style()
799 {
800 let italic_str = String::from("yo").italic();
801 let input = format!(
802 "start 1:{} 2:{} 3:{} end",
803 italic_str, italic_str, italic_str
804 );
805 let style = input.blue();
806
807 let output = style.escape_inner_reset_sequences();
808 let blue = "\x1B[34m";
809 let italic = "\x1B[3m";
810 let reset = "\x1B[0m";
811 let expected = format!(
812 "start 1:{}yo{}{} 2:{}yo{}{} 3:{}yo{}{} end",
813 italic, reset, blue, italic, reset, blue, italic, reset, blue
814 );
815
816 println!("first: {}\nsecond: {}", expected, output);
817
818 assert_eq!(expected, output);
819 }
820
821 #[test]
822 fn color_fn() {
823 assert_eq!("blue".blue(), "blue".color("blue"))
824 }
825
826 #[test]
827 fn on_color_fn() {
828 assert_eq!("blue".on_blue(), "blue".on_color("blue"))
829 }
830
831 #[test]
832 fn bright_color_fn() {
833 assert_eq!("blue".bright_blue(), "blue".color("bright blue"))
834 }
835
836 #[test]
837 fn on_bright_color_fn() {
838 assert_eq!("blue".on_bright_blue(), "blue".on_color("bright blue"))
839 }
840
841 #[test]
842 fn exposing_tests() {
843 let cstring = "".red();
844 assert_eq!(cstring.fgcolor(), Some(Color::Red));
845 assert_eq!(cstring.bgcolor(), None);
846
847 let cstring = cstring.clear();
848 assert_eq!(cstring.fgcolor(), None);
849 assert_eq!(cstring.bgcolor(), None);
850
851 let cstring = cstring.blue().on_bright_yellow();
852 assert_eq!(cstring.fgcolor(), Some(Color::Blue));
853 assert_eq!(cstring.bgcolor(), Some(Color::BrightYellow));
854
855 let cstring = cstring.bold().italic();
856 assert_eq!(cstring.fgcolor(), Some(Color::Blue));
857 assert_eq!(cstring.bgcolor(), Some(Color::BrightYellow));
858 assert_eq!(cstring.style().contains(Styles::Bold), true);
859 assert_eq!(cstring.style().contains(Styles::Italic), true);
860 assert_eq!(cstring.style().contains(Styles::Dimmed), false);
861 }
862}
863