1#![warn(missing_docs)]
31
32#[macro_use]
33extern crate lazy_static;
34
35#[cfg(test)]
36extern crate rspec;
37
38mod color;
39pub mod control;
40mod error;
41mod style;
42
43pub use self::customcolors::CustomColor;
44
45pub mod customcolors;
47
48pub use color::*;
49
50use std::{
51 borrow::Cow,
52 error::Error,
53 fmt,
54 ops::{Deref, DerefMut},
55};
56
57pub use style::{Style, Styles};
58
59#[derive(Clone, Debug, Default, PartialEq, Eq)]
126#[non_exhaustive]
127pub struct ColoredString {
128 pub input: String,
130 pub fgcolor: Option<Color>,
132 pub bgcolor: Option<Color>,
135 pub style: style::Style,
138}
139
140#[allow(missing_docs)]
141#[derive(Copy, Clone, Debug)]
142pub enum AnsiOrCustom {
143 Ansi(u8),
144 Custom(CustomColor),
145}
146
147impl From<u8> for AnsiOrCustom {
148 fn from(code: u8) -> Self {
149 AnsiOrCustom::Ansi(code)
150 }
151}
152
153#[allow(missing_docs)]
158pub trait Colorize {
159 fn black(self) -> ColoredString
161 where
162 Self: Sized,
163 {
164 self.color(Color::Black)
165 }
166 fn red(self) -> ColoredString
167 where
168 Self: Sized,
169 {
170 self.color(Color::Red)
171 }
172 fn green(self) -> ColoredString
173 where
174 Self: Sized,
175 {
176 self.color(Color::Green)
177 }
178 fn yellow(self) -> ColoredString
179 where
180 Self: Sized,
181 {
182 self.color(Color::Yellow)
183 }
184 fn blue(self) -> ColoredString
185 where
186 Self: Sized,
187 {
188 self.color(Color::Blue)
189 }
190 fn magenta(self) -> ColoredString
191 where
192 Self: Sized,
193 {
194 self.color(Color::Magenta)
195 }
196 fn purple(self) -> ColoredString
197 where
198 Self: Sized,
199 {
200 self.color(Color::Magenta)
201 }
202 fn cyan(self) -> ColoredString
203 where
204 Self: Sized,
205 {
206 self.color(Color::Cyan)
207 }
208 fn white(self) -> ColoredString
209 where
210 Self: Sized,
211 {
212 self.color(Color::White)
213 }
214 fn bright_black(self) -> ColoredString
215 where
216 Self: Sized,
217 {
218 self.color(Color::BrightBlack)
219 }
220 fn bright_red(self) -> ColoredString
221 where
222 Self: Sized,
223 {
224 self.color(Color::BrightRed)
225 }
226 fn bright_green(self) -> ColoredString
227 where
228 Self: Sized,
229 {
230 self.color(Color::BrightGreen)
231 }
232 fn bright_yellow(self) -> ColoredString
233 where
234 Self: Sized,
235 {
236 self.color(Color::BrightYellow)
237 }
238 fn bright_blue(self) -> ColoredString
239 where
240 Self: Sized,
241 {
242 self.color(Color::BrightBlue)
243 }
244 fn bright_magenta(self) -> ColoredString
245 where
246 Self: Sized,
247 {
248 self.color(Color::BrightMagenta)
249 }
250 fn bright_purple(self) -> ColoredString
251 where
252 Self: Sized,
253 {
254 self.color(Color::BrightMagenta)
255 }
256 fn bright_cyan(self) -> ColoredString
257 where
258 Self: Sized,
259 {
260 self.color(Color::BrightCyan)
261 }
262 fn bright_white(self) -> ColoredString
263 where
264 Self: Sized,
265 {
266 self.color(Color::BrightWhite)
267 }
268 fn truecolor(self, r: u8, g: u8, b: u8) -> ColoredString
269 where
270 Self: Sized,
271 {
272 self.color(Color::TrueColor { r, g, b })
273 }
274 fn custom_color<T>(self, color: T) -> ColoredString
275 where
276 Self: Sized,
277 T: Into<CustomColor>,
278 {
279 let color = color.into();
280
281 self.color(Color::TrueColor {
282 r: color.r,
283 g: color.g,
284 b: color.b,
285 })
286 }
287 fn custom_color_or_ansi_color_code(self, color: impl Into<AnsiOrCustom>) -> ColoredString
288 where
289 Self: Sized,
290 {
291 match color.into() {
292 AnsiOrCustom::Ansi(color_value) => self.color(Color::AnsiColor(color_value)),
293 AnsiOrCustom::Custom(color_value) => self.color(Color::TrueColor {
294 r: color_value.r,
295 g: color_value.g,
296 b: color_value.b,
297 }),
298 }
299 }
300
301 fn color<S: Into<Color>>(self, color: S) -> ColoredString;
302 fn on_black(self) -> ColoredString
304 where
305 Self: Sized,
306 {
307 self.on_color(Color::Black)
308 }
309 fn on_red(self) -> ColoredString
310 where
311 Self: Sized,
312 {
313 self.on_color(Color::Red)
314 }
315 fn on_green(self) -> ColoredString
316 where
317 Self: Sized,
318 {
319 self.on_color(Color::Green)
320 }
321 fn on_yellow(self) -> ColoredString
322 where
323 Self: Sized,
324 {
325 self.on_color(Color::Yellow)
326 }
327 fn on_blue(self) -> ColoredString
328 where
329 Self: Sized,
330 {
331 self.on_color(Color::Blue)
332 }
333 fn on_magenta(self) -> ColoredString
334 where
335 Self: Sized,
336 {
337 self.on_color(Color::Magenta)
338 }
339 fn on_purple(self) -> ColoredString
340 where
341 Self: Sized,
342 {
343 self.on_color(Color::Magenta)
344 }
345 fn on_cyan(self) -> ColoredString
346 where
347 Self: Sized,
348 {
349 self.on_color(Color::Cyan)
350 }
351 fn on_white(self) -> ColoredString
352 where
353 Self: Sized,
354 {
355 self.on_color(Color::White)
356 }
357 fn on_bright_black(self) -> ColoredString
358 where
359 Self: Sized,
360 {
361 self.on_color(Color::BrightBlack)
362 }
363 fn on_bright_red(self) -> ColoredString
364 where
365 Self: Sized,
366 {
367 self.on_color(Color::BrightRed)
368 }
369 fn on_bright_green(self) -> ColoredString
370 where
371 Self: Sized,
372 {
373 self.on_color(Color::BrightGreen)
374 }
375 fn on_bright_yellow(self) -> ColoredString
376 where
377 Self: Sized,
378 {
379 self.on_color(Color::BrightYellow)
380 }
381 fn on_bright_blue(self) -> ColoredString
382 where
383 Self: Sized,
384 {
385 self.on_color(Color::BrightBlue)
386 }
387 fn on_bright_magenta(self) -> ColoredString
388 where
389 Self: Sized,
390 {
391 self.on_color(Color::BrightMagenta)
392 }
393 fn on_bright_purple(self) -> ColoredString
394 where
395 Self: Sized,
396 {
397 self.on_color(Color::BrightMagenta)
398 }
399 fn on_bright_cyan(self) -> ColoredString
400 where
401 Self: Sized,
402 {
403 self.on_color(Color::BrightCyan)
404 }
405 fn on_bright_white(self) -> ColoredString
406 where
407 Self: Sized,
408 {
409 self.on_color(Color::BrightWhite)
410 }
411 fn on_truecolor(self, r: u8, g: u8, b: u8) -> ColoredString
412 where
413 Self: Sized,
414 {
415 self.on_color(Color::TrueColor { r, g, b })
416 }
417 fn on_custom_color<T>(self, color: T) -> ColoredString
418 where
419 Self: Sized,
420 T: Into<CustomColor>,
421 {
422 let color = color.into();
423
424 self.on_color(Color::TrueColor {
425 r: color.r,
426 g: color.g,
427 b: color.b,
428 })
429 }
430 fn on_color<S: Into<Color>>(self, color: S) -> ColoredString;
431 fn clear(self) -> ColoredString;
433 fn normal(self) -> ColoredString;
434 fn bold(self) -> ColoredString;
435 fn dimmed(self) -> ColoredString;
436 fn italic(self) -> ColoredString;
437 fn underline(self) -> ColoredString;
438 fn blink(self) -> ColoredString;
439 #[deprecated(since = "1.5.2", note = "Users should use reversed instead")]
440 fn reverse(self) -> ColoredString;
441 fn reversed(self) -> ColoredString;
442 fn hidden(self) -> ColoredString;
443 fn strikethrough(self) -> ColoredString;
444}
445
446impl ColoredString {
447 #[deprecated(note = "Deprecated due to the exposing of the fgcolor struct field.")]
457 pub fn fgcolor(&self) -> Option<Color> {
458 self.fgcolor.as_ref().copied()
459 }
460
461 #[deprecated(note = "Deprecated due to the exposing of the bgcolor struct field.")]
471 pub fn bgcolor(&self) -> Option<Color> {
472 self.bgcolor.as_ref().copied()
473 }
474
475 #[deprecated(note = "Deprecated due to the exposing of the style struct field.")]
485 pub fn style(&self) -> style::Style {
486 self.style
487 }
488
489 pub fn clear_fgcolor(&mut self) {
492 self.fgcolor = None;
493 }
494
495 pub fn clear_bgcolor(&mut self) {
497 self.bgcolor = None;
498 }
499
500 pub fn clear_style(&mut self) {
503 self.style = Style::default();
504 }
505
506 pub fn is_plain(&self) -> bool {
516 self.bgcolor.is_none() && self.fgcolor.is_none() && self.style == style::CLEAR
517 }
518
519 #[cfg(not(feature = "no-color"))]
520 fn has_colors(&self) -> bool {
521 control::SHOULD_COLORIZE.should_colorize()
522 }
523
524 #[cfg(feature = "no-color")]
525 fn has_colors(&self) -> bool {
526 false
527 }
528
529 fn compute_style(&self) -> String {
530 if !self.has_colors() || self.is_plain() {
531 return String::new();
532 }
533
534 let mut res = String::from("\x1B[");
535 let mut has_wrote = if self.style != style::CLEAR {
536 res.push_str(&self.style.to_str());
537 true
538 } else {
539 false
540 };
541
542 if let Some(ref bgcolor) = self.bgcolor {
543 if has_wrote {
544 res.push(';');
545 }
546
547 res.push_str(&bgcolor.to_bg_str());
548 has_wrote = true;
549 }
550
551 if let Some(ref fgcolor) = self.fgcolor {
552 if has_wrote {
553 res.push(';');
554 }
555
556 res.push_str(&fgcolor.to_fg_str());
557 }
558
559 res.push('m');
560 res
561 }
562
563 fn escape_inner_reset_sequences(&self) -> Cow<str> {
564 if !self.has_colors() || self.is_plain() {
565 return self.input.as_str().into();
566 }
567
568 let reset = "\x1B[0m";
570 let style = self.compute_style();
571 let matches: Vec<usize> = self
572 .input
573 .match_indices(reset)
574 .map(|(idx, _)| idx)
575 .collect();
576 if matches.is_empty() {
577 return self.input.as_str().into();
578 }
579
580 let mut input = self.input.clone();
581 input.reserve(matches.len() * style.len());
582
583 for (idx_in_matches, offset) in matches.into_iter().enumerate() {
584 let mut offset = offset + reset.len() + idx_in_matches * style.len();
587
588 for cchar in style.chars() {
589 input.insert(offset, cchar);
590 offset += 1;
591 }
592 }
593
594 input.into()
595 }
596}
597
598impl Deref for ColoredString {
599 type Target = str;
600 fn deref(&self) -> &Self::Target {
601 &self.input
602 }
603}
604
605impl DerefMut for ColoredString {
606 fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
607 &mut self.input
608 }
609}
610
611impl From<String> for ColoredString {
612 fn from(s: String) -> Self {
613 ColoredString {
614 input: s,
615 ..ColoredString::default()
616 }
617 }
618}
619
620impl<'a> From<&'a str> for ColoredString {
621 fn from(s: &'a str) -> Self {
622 ColoredString {
623 input: String::from(s),
624 ..ColoredString::default()
625 }
626 }
627}
628
629impl Colorize for ColoredString {
630 fn color<S: Into<Color>>(mut self, color: S) -> ColoredString {
631 self.fgcolor = Some(color.into());
632 self
633 }
634 fn on_color<S: Into<Color>>(mut self, color: S) -> ColoredString {
635 self.bgcolor = Some(color.into());
636 self
637 }
638
639 fn clear(self) -> ColoredString {
640 ColoredString {
641 input: self.input,
642 ..ColoredString::default()
643 }
644 }
645 fn normal(self) -> ColoredString {
646 self.clear()
647 }
648 fn bold(mut self) -> ColoredString {
649 self.style.add(style::Styles::Bold);
650 self
651 }
652 fn dimmed(mut self) -> ColoredString {
653 self.style.add(style::Styles::Dimmed);
654 self
655 }
656 fn italic(mut self) -> ColoredString {
657 self.style.add(style::Styles::Italic);
658 self
659 }
660 fn underline(mut self) -> ColoredString {
661 self.style.add(style::Styles::Underline);
662 self
663 }
664 fn blink(mut self) -> ColoredString {
665 self.style.add(style::Styles::Blink);
666 self
667 }
668 fn reverse(self) -> ColoredString {
669 self.reversed()
670 }
671 fn reversed(mut self) -> ColoredString {
672 self.style.add(style::Styles::Reversed);
673 self
674 }
675 fn hidden(mut self) -> ColoredString {
676 self.style.add(style::Styles::Hidden);
677 self
678 }
679 fn strikethrough(mut self) -> ColoredString {
680 self.style.add(style::Styles::Strikethrough);
681 self
682 }
683}
684
685impl<'a> Colorize for &'a str {
686 fn color<S: Into<Color>>(self, color: S) -> ColoredString {
687 ColoredString {
688 fgcolor: Some(color.into()),
689 input: String::from(self),
690 ..ColoredString::default()
691 }
692 }
693
694 fn on_color<S: Into<Color>>(self, color: S) -> ColoredString {
695 ColoredString {
696 bgcolor: Some(color.into()),
697 input: String::from(self),
698 ..ColoredString::default()
699 }
700 }
701
702 fn clear(self) -> ColoredString {
703 ColoredString {
704 input: String::from(self),
705 style: style::CLEAR,
706 ..ColoredString::default()
707 }
708 }
709 fn normal(self) -> ColoredString {
710 self.clear()
711 }
712 fn bold(self) -> ColoredString {
713 ColoredString::from(self).bold()
714 }
715 fn dimmed(self) -> ColoredString {
716 ColoredString::from(self).dimmed()
717 }
718 fn italic(self) -> ColoredString {
719 ColoredString::from(self).italic()
720 }
721 fn underline(self) -> ColoredString {
722 ColoredString::from(self).underline()
723 }
724 fn blink(self) -> ColoredString {
725 ColoredString::from(self).blink()
726 }
727 fn reverse(self) -> ColoredString {
728 self.reversed()
729 }
730 fn reversed(self) -> ColoredString {
731 ColoredString::from(self).reversed()
732 }
733 fn hidden(self) -> ColoredString {
734 ColoredString::from(self).hidden()
735 }
736 fn strikethrough(self) -> ColoredString {
737 ColoredString::from(self).strikethrough()
738 }
739}
740
741impl fmt::Display for ColoredString {
742 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
743 if !self.has_colors() || self.is_plain() {
744 return <String as fmt::Display>::fmt(&self.input, f);
745 }
746
747 let escaped_input = self.escape_inner_reset_sequences();
749
750 f.write_str(&self.compute_style())?;
751 escaped_input.fmt(f)?;
752 f.write_str("\x1B[0m")?;
753 Ok(())
754 }
755}
756
757impl From<ColoredString> for Box<dyn Error> {
758 fn from(cs: ColoredString) -> Box<dyn Error> {
759 Box::from(error::ColoredStringError(cs))
760 }
761}
762
763#[cfg(test)]
764mod tests {
765 use super::*;
766 use std::{error::Error, fmt::Write};
767
768 #[test]
769 fn formatting() {
770 assert!(format!("{:40}", "".blue()).len() >= 40);
772 assert_eq!(
774 format!("{:1.1}", "toto".blue()).len(),
775 format!("{:1.1}", "1".blue()).len()
776 )
777 }
778
779 #[test]
780 fn it_works() -> Result<(), Box<dyn Error>> {
781 let mut buf = String::new();
782 let toto = "toto";
783 writeln!(&mut buf, "{}", toto.red())?;
784 writeln!(&mut buf, "{}", String::from(toto).red())?;
785 writeln!(&mut buf, "{}", toto.blue())?;
786
787 writeln!(&mut buf, "blue style ****")?;
788 writeln!(&mut buf, "{}", toto.bold())?;
789 writeln!(&mut buf, "{}", "yeah ! Red bold !".red().bold())?;
790 writeln!(&mut buf, "{}", "yeah ! Yellow bold !".bold().yellow())?;
791 writeln!(&mut buf, "{}", toto.bold().blue())?;
792 writeln!(&mut buf, "{}", toto.blue().bold())?;
793 writeln!(&mut buf, "{}", toto.blue().bold().underline())?;
794 writeln!(&mut buf, "{}", toto.blue().italic())?;
795 writeln!(&mut buf, "******")?;
796 writeln!(&mut buf, "test clearing")?;
797 writeln!(&mut buf, "{}", "red cleared".red().clear())?;
798 writeln!(&mut buf, "{}", "bold cyan cleared".bold().cyan().clear())?;
799 writeln!(&mut buf, "******")?;
800 writeln!(&mut buf, "Bg tests")?;
801 writeln!(&mut buf, "{}", toto.green().on_blue())?;
802 writeln!(&mut buf, "{}", toto.on_magenta().yellow())?;
803 writeln!(&mut buf, "{}", toto.purple().on_yellow())?;
804 writeln!(&mut buf, "{}", toto.magenta().on_white())?;
805 writeln!(&mut buf, "{}", toto.cyan().on_green())?;
806 writeln!(&mut buf, "{}", toto.black().on_white())?;
807 writeln!(&mut buf, "******")?;
808 writeln!(&mut buf, "{}", toto.green())?;
809 writeln!(&mut buf, "{}", toto.yellow())?;
810 writeln!(&mut buf, "{}", toto.purple())?;
811 writeln!(&mut buf, "{}", toto.magenta())?;
812 writeln!(&mut buf, "{}", toto.cyan())?;
813 writeln!(&mut buf, "{}", toto.white())?;
814 writeln!(&mut buf, "{}", toto.white().red().blue().green())?;
815 writeln!(&mut buf, "{}", toto.truecolor(255, 0, 0))?;
816 writeln!(&mut buf, "{}", toto.truecolor(255, 255, 0))?;
817 writeln!(&mut buf, "{}", toto.on_truecolor(0, 80, 80))?;
818 writeln!(&mut buf, "{}", toto.custom_color((255, 255, 0)))?;
819 writeln!(&mut buf, "{}", toto.on_custom_color((0, 80, 80)))?;
820 #[cfg(feature = "no-color")]
821 insta::assert_snapshot!("it_works_no_color", buf);
822 #[cfg(not(feature = "no-color"))]
823 insta::assert_snapshot!("it_works", buf);
824 Ok(())
825 }
826
827 #[test]
828 fn compute_style_empty_string() {
829 assert_eq!("", "".clear().compute_style());
830 }
831
832 #[cfg_attr(feature = "no-color", ignore)]
833 #[test]
834 fn compute_style_simple_fg_blue() {
835 let blue = "\x1B[34m";
836
837 assert_eq!(blue, "".blue().compute_style());
838 }
839
840 #[cfg_attr(feature = "no-color", ignore)]
841 #[test]
842 fn compute_style_simple_bg_blue() {
843 let on_blue = "\x1B[44m";
844
845 assert_eq!(on_blue, "".on_blue().compute_style());
846 }
847
848 #[cfg_attr(feature = "no-color", ignore)]
849 #[test]
850 fn compute_style_blue_on_blue() {
851 let blue_on_blue = "\x1B[44;34m";
852
853 assert_eq!(blue_on_blue, "".blue().on_blue().compute_style());
854 }
855
856 #[cfg_attr(feature = "no-color", ignore)]
857 #[test]
858 fn compute_style_simple_fg_bright_blue() {
859 let blue = "\x1B[94m";
860
861 assert_eq!(blue, "".bright_blue().compute_style());
862 }
863
864 #[cfg_attr(feature = "no-color", ignore)]
865 #[test]
866 fn compute_style_simple_bg_bright_blue() {
867 let on_blue = "\x1B[104m";
868
869 assert_eq!(on_blue, "".on_bright_blue().compute_style());
870 }
871
872 #[cfg_attr(feature = "no-color", ignore)]
873 #[test]
874 fn compute_style_bright_blue_on_bright_blue() {
875 let blue_on_blue = "\x1B[104;94m";
876
877 assert_eq!(
878 blue_on_blue,
879 "".bright_blue().on_bright_blue().compute_style()
880 );
881 }
882
883 #[cfg_attr(feature = "no-color", ignore)]
884 #[test]
885 fn compute_style_simple_bold() {
886 let bold = "\x1B[1m";
887
888 assert_eq!(bold, "".bold().compute_style());
889 }
890
891 #[cfg_attr(feature = "no-color", ignore)]
892 #[test]
893 fn compute_style_blue_bold() {
894 let blue_bold = "\x1B[1;34m";
895
896 assert_eq!(blue_bold, "".blue().bold().compute_style());
897 }
898
899 #[cfg_attr(feature = "no-color", ignore)]
900 #[test]
901 fn compute_style_blue_bold_on_blue() {
902 let blue_bold_on_blue = "\x1B[1;44;34m";
903
904 assert_eq!(
905 blue_bold_on_blue,
906 "".blue().bold().on_blue().compute_style()
907 );
908 }
909
910 #[test]
911 fn escape_reset_sequence_spec_should_do_nothing_on_empty_strings() {
912 let style = ColoredString::default();
913 let expected = String::new();
914
915 let output = style.escape_inner_reset_sequences();
916
917 assert_eq!(expected, output);
918 }
919
920 #[test]
921 fn escape_reset_sequence_spec_should_do_nothing_on_string_with_no_reset() {
922 let style = ColoredString {
923 input: String::from("hello world !"),
924 ..ColoredString::default()
925 };
926
927 let expected = String::from("hello world !");
928 let output = style.escape_inner_reset_sequences();
929
930 assert_eq!(expected, output);
931 }
932
933 #[cfg_attr(feature = "no-color", ignore)]
934 #[test]
935 fn escape_reset_sequence_spec_should_replace_inner_reset_sequence_with_current_style() {
936 let input = format!("start {} end", String::from("hello world !").red());
937 let style = input.blue();
938
939 let output = style.escape_inner_reset_sequences();
940 let blue = "\x1B[34m";
941 let red = "\x1B[31m";
942 let reset = "\x1B[0m";
943 let expected = format!("start {}hello world !{}{} end", red, reset, blue);
944 assert_eq!(expected, output);
945 }
946
947 #[cfg_attr(feature = "no-color", ignore)]
948 #[test]
949 fn escape_reset_sequence_spec_should_replace_multiple_inner_reset_sequences_with_current_style()
950 {
951 let italic_str = String::from("yo").italic();
952 let input = format!(
953 "start 1:{} 2:{} 3:{} end",
954 italic_str, italic_str, italic_str
955 );
956 let style = input.blue();
957
958 let output = style.escape_inner_reset_sequences();
959 let blue = "\x1B[34m";
960 let italic = "\x1B[3m";
961 let reset = "\x1B[0m";
962 let expected = format!(
963 "start 1:{}yo{}{} 2:{}yo{}{} 3:{}yo{}{} end",
964 italic, reset, blue, italic, reset, blue, italic, reset, blue
965 );
966
967 println!("first: {}\nsecond: {}", expected, output);
968
969 assert_eq!(expected, output);
970 }
971
972 #[test]
973 fn color_fn() {
974 assert_eq!("blue".blue(), "blue".color("blue"))
975 }
976
977 #[test]
978 fn on_color_fn() {
979 assert_eq!("blue".on_blue(), "blue".on_color("blue"))
980 }
981
982 #[test]
983 fn bright_color_fn() {
984 assert_eq!("blue".bright_blue(), "blue".color("bright blue"))
985 }
986
987 #[test]
988 fn on_bright_color_fn() {
989 assert_eq!("blue".on_bright_blue(), "blue".on_color("bright blue"))
990 }
991
992 #[test]
993 fn exposing_tests() {
994 #![allow(deprecated)]
995
996 let cstring = "".red();
997 assert_eq!(cstring.fgcolor(), Some(Color::Red));
998 assert_eq!(cstring.bgcolor(), None);
999
1000 let cstring = cstring.clear();
1001 assert_eq!(cstring.fgcolor(), None);
1002 assert_eq!(cstring.bgcolor(), None);
1003
1004 let cstring = cstring.blue().on_bright_yellow();
1005 assert_eq!(cstring.fgcolor(), Some(Color::Blue));
1006 assert_eq!(cstring.bgcolor(), Some(Color::BrightYellow));
1007
1008 let cstring = cstring.bold().italic();
1009 assert_eq!(cstring.fgcolor(), Some(Color::Blue));
1010 assert_eq!(cstring.bgcolor(), Some(Color::BrightYellow));
1011 assert_eq!(cstring.style().contains(Styles::Bold), true);
1012 assert_eq!(cstring.style().contains(Styles::Italic), true);
1013 assert_eq!(cstring.style().contains(Styles::Dimmed), false);
1014 }
1015}