colored/
lib.rs

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//! Note: The methods of [`Colorize`], when used on [`str`]'s, return
27//! [`ColoredString`]'s. See [`ColoredString`] to learn more about them and
28//! what you can do with them beyond continue to use [`Colorize`] to further
29//! modify them.
30#![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
45/// Custom colors support.
46pub 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/// A string that may have color and/or style applied to it.
60///
61/// Commonly created via calling the methods of [`Colorize`] on a &str.
62/// All methods of [`Colorize`] either create a new `ColoredString` from
63/// the type called on or modify a callee `ColoredString`. See
64/// [`Colorize`] for more.
65///
66/// The primary usage of `ColoredString`'s is as a way to take text,
67/// apply colors and miscillaneous styling to it (such as bold or
68/// underline), and then use it to create formatted strings that print
69/// to the console with the special styling applied.
70///
71/// ## Usage
72///
73/// As stated, `ColoredString`'s, once created, can be printed to the
74/// console with their colors and style or turned into a string
75/// containing special console codes that has the same effect.
76/// This is made easy via `ColoredString`'s implementations of
77/// [`Display`](std::fmt::Display) and [`ToString`] for those purposes
78/// respectively.
79///
80/// Printing a `ColoredString` with its style is as easy as:
81///
82/// ```
83/// # use colored::*;
84/// let cstring: ColoredString = "Bold and Red!".bold().red();
85/// println!("{}", cstring);
86/// ```
87///
88/// ## Manipulating the coloring/style of a `ColoredString`
89///
90/// Getting or changing the foreground color, background color, and or
91/// style of a `ColoredString` is as easy as manually reading / modifying
92/// the fields of `ColoredString`.
93///
94/// ```
95/// # use colored::*;
96/// let mut red_text = "Red".red();
97/// // Changing color using re-assignment and [`Colorize`]:
98/// red_text = red_text.blue();
99/// // Manipulating fields of `ColoredString` in-place:
100/// red_text.fgcolor = Some(Color::Blue);
101///
102/// let styled_text1 = "Bold".bold();
103/// let styled_text2 = "Italic".italic();
104/// let mut styled_text3 = ColoredString::from("Bold and Italic");
105/// styled_text3.style = styled_text1.style | styled_text2.style;
106/// ```
107///
108/// ## Modifying the text of a `ColoredString`
109///
110/// Modifying the text is as easy as modifying the `input` field of
111/// `ColoredString`...
112///
113/// ```
114/// # use colored::*;
115/// let mut colored_text = "Magenta".magenta();
116/// colored_text = colored_text.blue();
117/// colored_text.input = "Blue".to_string();
118/// // Note: The above is inefficient and `colored_text.input.replace_range(.., "Blue")` would
119/// // be more proper. This is just for example.
120///
121/// assert_eq!(&*colored_text, "Blue");
122/// ```
123///
124/// Notice how this process preserves the coloring and style.
125#[derive(Clone, Debug, Default, PartialEq, Eq)]
126#[non_exhaustive]
127pub struct ColoredString {
128    /// The plain text that will have color and style applied to it.
129    pub input: String,
130    /// The color of the text as it will be printed.
131    pub fgcolor: Option<Color>,
132    /// The background color (if any). None means that the text will be printed
133    /// without a special background.
134    pub bgcolor: Option<Color>,
135    /// Any special styling to be applied to the text (see Styles for a list of
136    /// available options).
137    pub style: style::Style,
138}
139
140/// The trait that enables something to be given color.
141///
142/// You can use `colored` effectively simply by importing this trait
143/// and then using its methods on `String` and `&str`.
144#[allow(missing_docs)]
145pub trait Colorize {
146    // Font Colors
147    fn black(self) -> ColoredString
148    where
149        Self: Sized,
150    {
151        self.color(Color::Black)
152    }
153    fn red(self) -> ColoredString
154    where
155        Self: Sized,
156    {
157        self.color(Color::Red)
158    }
159    fn green(self) -> ColoredString
160    where
161        Self: Sized,
162    {
163        self.color(Color::Green)
164    }
165    fn yellow(self) -> ColoredString
166    where
167        Self: Sized,
168    {
169        self.color(Color::Yellow)
170    }
171    fn blue(self) -> ColoredString
172    where
173        Self: Sized,
174    {
175        self.color(Color::Blue)
176    }
177    fn magenta(self) -> ColoredString
178    where
179        Self: Sized,
180    {
181        self.color(Color::Magenta)
182    }
183    fn purple(self) -> ColoredString
184    where
185        Self: Sized,
186    {
187        self.color(Color::Magenta)
188    }
189    fn cyan(self) -> ColoredString
190    where
191        Self: Sized,
192    {
193        self.color(Color::Cyan)
194    }
195    fn white(self) -> ColoredString
196    where
197        Self: Sized,
198    {
199        self.color(Color::White)
200    }
201    fn bright_black(self) -> ColoredString
202    where
203        Self: Sized,
204    {
205        self.color(Color::BrightBlack)
206    }
207    fn bright_red(self) -> ColoredString
208    where
209        Self: Sized,
210    {
211        self.color(Color::BrightRed)
212    }
213    fn bright_green(self) -> ColoredString
214    where
215        Self: Sized,
216    {
217        self.color(Color::BrightGreen)
218    }
219    fn bright_yellow(self) -> ColoredString
220    where
221        Self: Sized,
222    {
223        self.color(Color::BrightYellow)
224    }
225    fn bright_blue(self) -> ColoredString
226    where
227        Self: Sized,
228    {
229        self.color(Color::BrightBlue)
230    }
231    fn bright_magenta(self) -> ColoredString
232    where
233        Self: Sized,
234    {
235        self.color(Color::BrightMagenta)
236    }
237    fn bright_purple(self) -> ColoredString
238    where
239        Self: Sized,
240    {
241        self.color(Color::BrightMagenta)
242    }
243    fn bright_cyan(self) -> ColoredString
244    where
245        Self: Sized,
246    {
247        self.color(Color::BrightCyan)
248    }
249    fn bright_white(self) -> ColoredString
250    where
251        Self: Sized,
252    {
253        self.color(Color::BrightWhite)
254    }
255    fn truecolor(self, r: u8, g: u8, b: u8) -> ColoredString
256    where
257        Self: Sized,
258    {
259        self.color(Color::TrueColor { r, g, b })
260    }
261    fn custom_color<T>(self, color: T) -> ColoredString
262    where
263        Self: Sized,
264        T: Into<CustomColor>,
265    {
266        let color = color.into();
267
268        self.color(Color::TrueColor {
269            r: color.r,
270            g: color.g,
271            b: color.b,
272        })
273    }
274    fn color<S: Into<Color>>(self, color: S) -> ColoredString;
275    // Background Colors
276    fn on_black(self) -> ColoredString
277    where
278        Self: Sized,
279    {
280        self.on_color(Color::Black)
281    }
282    fn on_red(self) -> ColoredString
283    where
284        Self: Sized,
285    {
286        self.on_color(Color::Red)
287    }
288    fn on_green(self) -> ColoredString
289    where
290        Self: Sized,
291    {
292        self.on_color(Color::Green)
293    }
294    fn on_yellow(self) -> ColoredString
295    where
296        Self: Sized,
297    {
298        self.on_color(Color::Yellow)
299    }
300    fn on_blue(self) -> ColoredString
301    where
302        Self: Sized,
303    {
304        self.on_color(Color::Blue)
305    }
306    fn on_magenta(self) -> ColoredString
307    where
308        Self: Sized,
309    {
310        self.on_color(Color::Magenta)
311    }
312    fn on_purple(self) -> ColoredString
313    where
314        Self: Sized,
315    {
316        self.on_color(Color::Magenta)
317    }
318    fn on_cyan(self) -> ColoredString
319    where
320        Self: Sized,
321    {
322        self.on_color(Color::Cyan)
323    }
324    fn on_white(self) -> ColoredString
325    where
326        Self: Sized,
327    {
328        self.on_color(Color::White)
329    }
330    fn on_bright_black(self) -> ColoredString
331    where
332        Self: Sized,
333    {
334        self.on_color(Color::BrightBlack)
335    }
336    fn on_bright_red(self) -> ColoredString
337    where
338        Self: Sized,
339    {
340        self.on_color(Color::BrightRed)
341    }
342    fn on_bright_green(self) -> ColoredString
343    where
344        Self: Sized,
345    {
346        self.on_color(Color::BrightGreen)
347    }
348    fn on_bright_yellow(self) -> ColoredString
349    where
350        Self: Sized,
351    {
352        self.on_color(Color::BrightYellow)
353    }
354    fn on_bright_blue(self) -> ColoredString
355    where
356        Self: Sized,
357    {
358        self.on_color(Color::BrightBlue)
359    }
360    fn on_bright_magenta(self) -> ColoredString
361    where
362        Self: Sized,
363    {
364        self.on_color(Color::BrightMagenta)
365    }
366    fn on_bright_purple(self) -> ColoredString
367    where
368        Self: Sized,
369    {
370        self.on_color(Color::BrightMagenta)
371    }
372    fn on_bright_cyan(self) -> ColoredString
373    where
374        Self: Sized,
375    {
376        self.on_color(Color::BrightCyan)
377    }
378    fn on_bright_white(self) -> ColoredString
379    where
380        Self: Sized,
381    {
382        self.on_color(Color::BrightWhite)
383    }
384    fn on_truecolor(self, r: u8, g: u8, b: u8) -> ColoredString
385    where
386        Self: Sized,
387    {
388        self.on_color(Color::TrueColor { r, g, b })
389    }
390    fn on_custom_color<T>(self, color: T) -> ColoredString
391    where
392        Self: Sized,
393        T: Into<CustomColor>,
394    {
395        let color = color.into();
396
397        self.on_color(Color::TrueColor {
398            r: color.r,
399            g: color.g,
400            b: color.b,
401        })
402    }
403    fn on_color<S: Into<Color>>(self, color: S) -> ColoredString;
404    // Styles
405    fn clear(self) -> ColoredString;
406    fn normal(self) -> ColoredString;
407    fn bold(self) -> ColoredString;
408    fn dimmed(self) -> ColoredString;
409    fn italic(self) -> ColoredString;
410    fn underline(self) -> ColoredString;
411    fn blink(self) -> ColoredString;
412    #[deprecated(since = "1.5.2", note = "Users should use reversed instead")]
413    fn reverse(self) -> ColoredString;
414    fn reversed(self) -> ColoredString;
415    fn hidden(self) -> ColoredString;
416    fn strikethrough(self) -> ColoredString;
417}
418
419impl ColoredString {
420    /// Get the current background color applied.
421    ///
422    /// ```rust
423    /// # use colored::*;
424    /// let cstr = "".blue();
425    /// assert_eq!(cstr.fgcolor(), Some(Color::Blue));
426    /// let cstr = cstr.clear();
427    /// assert_eq!(cstr.fgcolor(), None);
428    /// ```
429    #[deprecated(note = "Deprecated due to the exposing of the fgcolor struct field.")]
430    pub fn fgcolor(&self) -> Option<Color> {
431        self.fgcolor.as_ref().copied()
432    }
433
434    /// Get the current background color applied.
435    ///
436    /// ```rust
437    /// # use colored::*;
438    /// let cstr = "".on_blue();
439    /// assert_eq!(cstr.bgcolor(), Some(Color::Blue));
440    /// let cstr = cstr.clear();
441    /// assert_eq!(cstr.bgcolor(), None);
442    /// ```
443    #[deprecated(note = "Deprecated due to the exposing of the bgcolor struct field.")]
444    pub fn bgcolor(&self) -> Option<Color> {
445        self.bgcolor.as_ref().copied()
446    }
447
448    /// Get the current [`Style`] which can be check if it contains a [`Styles`].
449    ///
450    /// ```rust
451    /// # use colored::*;
452    /// let colored = "".bold().italic();
453    /// assert_eq!(colored.style().contains(Styles::Bold), true);
454    /// assert_eq!(colored.style().contains(Styles::Italic), true);
455    /// assert_eq!(colored.style().contains(Styles::Dimmed), false);
456    /// ```
457    #[deprecated(note = "Deprecated due to the exposing of the style struct field.")]
458    pub fn style(&self) -> style::Style {
459        self.style
460    }
461
462    /// Clears foreground coloring on this `ColoredString`, meaning that it
463    /// will be printed with the default terminal text color.
464    pub fn clear_fgcolor(&mut self) {
465        self.fgcolor = None;
466    }
467
468    /// Gets rid of this `ColoredString`'s background.
469    pub fn clear_bgcolor(&mut self) {
470        self.bgcolor = None;
471    }
472
473    /// Clears any special styling and sets it back to the default (plain,
474    /// maybe colored, text).
475    pub fn clear_style(&mut self) {
476        self.style = Style::default();
477    }
478
479    /// Checks if the colored string has no color or styling.
480    ///
481    /// ```rust
482    /// # use colored::*;
483    /// let cstr = "".red();
484    /// assert_eq!(cstr.is_plain(), false);
485    /// let cstr = cstr.clear();
486    /// assert_eq!(cstr.is_plain(), true);
487    /// ```
488    pub fn is_plain(&self) -> bool {
489        self.bgcolor.is_none() && self.fgcolor.is_none() && self.style == style::CLEAR
490    }
491
492    #[cfg(not(feature = "no-color"))]
493    fn has_colors() -> bool {
494        control::SHOULD_COLORIZE.should_colorize()
495    }
496
497    #[cfg(feature = "no-color")]
498    fn has_colors() -> bool {
499        false
500    }
501
502    fn compute_style(&self) -> String {
503        if !ColoredString::has_colors() || self.is_plain() {
504            return String::new();
505        }
506
507        let mut res = String::from("\x1B[");
508        let mut has_wrote = if self.style != style::CLEAR {
509            res.push_str(&self.style.to_str());
510            true
511        } else {
512            false
513        };
514
515        if let Some(ref bgcolor) = self.bgcolor {
516            if has_wrote {
517                res.push(';');
518            }
519
520            res.push_str(&bgcolor.to_bg_str());
521            has_wrote = true;
522        }
523
524        if let Some(ref fgcolor) = self.fgcolor {
525            if has_wrote {
526                res.push(';');
527            }
528
529            res.push_str(&fgcolor.to_fg_str());
530        }
531
532        res.push('m');
533        res
534    }
535
536    fn escape_inner_reset_sequences(&self) -> Cow<str> {
537        if !ColoredString::has_colors() || self.is_plain() {
538            return self.input.as_str().into();
539        }
540
541        // TODO: BoyScoutRule
542        let reset = "\x1B[0m";
543        let style = self.compute_style();
544        let matches: Vec<usize> = self
545            .input
546            .match_indices(reset)
547            .map(|(idx, _)| idx)
548            .collect();
549        if matches.is_empty() {
550            return self.input.as_str().into();
551        }
552
553        let mut input = self.input.clone();
554        input.reserve(matches.len() * style.len());
555
556        for (idx_in_matches, offset) in matches.into_iter().enumerate() {
557            // shift the offset to the end of the reset sequence and take in account
558            // the number of matches we have escaped (which shift the index to insert)
559            let mut offset = offset + reset.len() + idx_in_matches * style.len();
560
561            for cchar in style.chars() {
562                input.insert(offset, cchar);
563                offset += 1;
564            }
565        }
566
567        input.into()
568    }
569}
570
571impl Deref for ColoredString {
572    type Target = str;
573    fn deref(&self) -> &Self::Target {
574        &self.input
575    }
576}
577
578impl DerefMut for ColoredString {
579    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
580        &mut self.input
581    }
582}
583
584impl From<String> for ColoredString {
585    fn from(s: String) -> Self {
586        ColoredString {
587            input: s,
588            ..ColoredString::default()
589        }
590    }
591}
592
593impl<'a> From<&'a str> for ColoredString {
594    fn from(s: &'a str) -> Self {
595        ColoredString {
596            input: String::from(s),
597            ..ColoredString::default()
598        }
599    }
600}
601
602impl Colorize for ColoredString {
603    fn color<S: Into<Color>>(mut self, color: S) -> ColoredString {
604        self.fgcolor = Some(color.into());
605        self
606    }
607    fn on_color<S: Into<Color>>(mut self, color: S) -> ColoredString {
608        self.bgcolor = Some(color.into());
609        self
610    }
611
612    fn clear(self) -> ColoredString {
613        ColoredString {
614            input: self.input,
615            ..ColoredString::default()
616        }
617    }
618    fn normal(self) -> ColoredString {
619        self.clear()
620    }
621    fn bold(mut self) -> ColoredString {
622        self.style.add(style::Styles::Bold);
623        self
624    }
625    fn dimmed(mut self) -> ColoredString {
626        self.style.add(style::Styles::Dimmed);
627        self
628    }
629    fn italic(mut self) -> ColoredString {
630        self.style.add(style::Styles::Italic);
631        self
632    }
633    fn underline(mut self) -> ColoredString {
634        self.style.add(style::Styles::Underline);
635        self
636    }
637    fn blink(mut self) -> ColoredString {
638        self.style.add(style::Styles::Blink);
639        self
640    }
641    fn reverse(self) -> ColoredString {
642        self.reversed()
643    }
644    fn reversed(mut self) -> ColoredString {
645        self.style.add(style::Styles::Reversed);
646        self
647    }
648    fn hidden(mut self) -> ColoredString {
649        self.style.add(style::Styles::Hidden);
650        self
651    }
652    fn strikethrough(mut self) -> ColoredString {
653        self.style.add(style::Styles::Strikethrough);
654        self
655    }
656}
657
658impl Colorize for &str {
659    fn color<S: Into<Color>>(self, color: S) -> ColoredString {
660        ColoredString {
661            fgcolor: Some(color.into()),
662            input: String::from(self),
663            ..ColoredString::default()
664        }
665    }
666
667    fn on_color<S: Into<Color>>(self, color: S) -> ColoredString {
668        ColoredString {
669            bgcolor: Some(color.into()),
670            input: String::from(self),
671            ..ColoredString::default()
672        }
673    }
674
675    fn clear(self) -> ColoredString {
676        ColoredString {
677            input: String::from(self),
678            style: style::CLEAR,
679            ..ColoredString::default()
680        }
681    }
682    fn normal(self) -> ColoredString {
683        self.clear()
684    }
685    fn bold(self) -> ColoredString {
686        ColoredString::from(self).bold()
687    }
688    fn dimmed(self) -> ColoredString {
689        ColoredString::from(self).dimmed()
690    }
691    fn italic(self) -> ColoredString {
692        ColoredString::from(self).italic()
693    }
694    fn underline(self) -> ColoredString {
695        ColoredString::from(self).underline()
696    }
697    fn blink(self) -> ColoredString {
698        ColoredString::from(self).blink()
699    }
700    fn reverse(self) -> ColoredString {
701        self.reversed()
702    }
703    fn reversed(self) -> ColoredString {
704        ColoredString::from(self).reversed()
705    }
706    fn hidden(self) -> ColoredString {
707        ColoredString::from(self).hidden()
708    }
709    fn strikethrough(self) -> ColoredString {
710        ColoredString::from(self).strikethrough()
711    }
712}
713
714impl fmt::Display for ColoredString {
715    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
716        if !ColoredString::has_colors() || self.is_plain() {
717            return <String as fmt::Display>::fmt(&self.input, f);
718        }
719
720        // XXX: see tests. Useful when nesting colored strings
721        let escaped_input = self.escape_inner_reset_sequences();
722
723        f.write_str(&self.compute_style())?;
724        escaped_input.fmt(f)?;
725        f.write_str("\x1B[0m")?;
726        Ok(())
727    }
728}
729
730impl From<ColoredString> for Box<dyn Error> {
731    fn from(cs: ColoredString) -> Box<dyn Error> {
732        Box::from(error::ColoredStringError(cs))
733    }
734}
735
736#[cfg(test)]
737mod tests {
738    use super::*;
739    use std::{error::Error, fmt::Write};
740
741    #[test]
742    fn formatting() {
743        // respect the formatting. Escape sequence add some padding so >= 40
744        assert!(format!("{:40}", "".blue()).len() >= 40);
745        // both should be truncated to 1 char before coloring
746        assert_eq!(
747            format!("{:1.1}", "toto".blue()).len(),
748            format!("{:1.1}", "1".blue()).len()
749        )
750    }
751
752    #[test]
753    fn it_works() -> Result<(), Box<dyn Error>> {
754        let mut buf = String::new();
755        let toto = "toto";
756        writeln!(&mut buf, "{}", toto.red())?;
757        writeln!(&mut buf, "{}", String::from(toto).red())?;
758        writeln!(&mut buf, "{}", toto.blue())?;
759
760        writeln!(&mut buf, "blue style ****")?;
761        writeln!(&mut buf, "{}", toto.bold())?;
762        writeln!(&mut buf, "{}", "yeah ! Red bold !".red().bold())?;
763        writeln!(&mut buf, "{}", "yeah ! Yellow bold !".bold().yellow())?;
764        writeln!(&mut buf, "{}", toto.bold().blue())?;
765        writeln!(&mut buf, "{}", toto.blue().bold())?;
766        writeln!(&mut buf, "{}", toto.blue().bold().underline())?;
767        writeln!(&mut buf, "{}", toto.blue().italic())?;
768        writeln!(&mut buf, "******")?;
769        writeln!(&mut buf, "test clearing")?;
770        writeln!(&mut buf, "{}", "red cleared".red().clear())?;
771        writeln!(&mut buf, "{}", "bold cyan cleared".bold().cyan().clear())?;
772        writeln!(&mut buf, "******")?;
773        writeln!(&mut buf, "Bg tests")?;
774        writeln!(&mut buf, "{}", toto.green().on_blue())?;
775        writeln!(&mut buf, "{}", toto.on_magenta().yellow())?;
776        writeln!(&mut buf, "{}", toto.purple().on_yellow())?;
777        writeln!(&mut buf, "{}", toto.magenta().on_white())?;
778        writeln!(&mut buf, "{}", toto.cyan().on_green())?;
779        writeln!(&mut buf, "{}", toto.black().on_white())?;
780        writeln!(&mut buf, "******")?;
781        writeln!(&mut buf, "{}", toto.green())?;
782        writeln!(&mut buf, "{}", toto.yellow())?;
783        writeln!(&mut buf, "{}", toto.purple())?;
784        writeln!(&mut buf, "{}", toto.magenta())?;
785        writeln!(&mut buf, "{}", toto.cyan())?;
786        writeln!(&mut buf, "{}", toto.white())?;
787        writeln!(&mut buf, "{}", toto.white().red().blue().green())?;
788        writeln!(&mut buf, "{}", toto.truecolor(255, 0, 0))?;
789        writeln!(&mut buf, "{}", toto.truecolor(255, 255, 0))?;
790        writeln!(&mut buf, "{}", toto.on_truecolor(0, 80, 80))?;
791        writeln!(&mut buf, "{}", toto.custom_color((255, 255, 0)))?;
792        writeln!(&mut buf, "{}", toto.on_custom_color((0, 80, 80)))?;
793        #[cfg(feature = "no-color")]
794        insta::assert_snapshot!("it_works_no_color", buf);
795        #[cfg(not(feature = "no-color"))]
796        insta::assert_snapshot!("it_works", buf);
797        Ok(())
798    }
799
800    #[test]
801    fn compute_style_empty_string() {
802        assert_eq!("", "".clear().compute_style());
803    }
804
805    #[cfg_attr(feature = "no-color", ignore)]
806    #[test]
807    fn compute_style_simple_fg_blue() {
808        let blue = "\x1B[34m";
809
810        assert_eq!(blue, "".blue().compute_style());
811    }
812
813    #[cfg_attr(feature = "no-color", ignore)]
814    #[test]
815    fn compute_style_simple_bg_blue() {
816        let on_blue = "\x1B[44m";
817
818        assert_eq!(on_blue, "".on_blue().compute_style());
819    }
820
821    #[cfg_attr(feature = "no-color", ignore)]
822    #[test]
823    fn compute_style_blue_on_blue() {
824        let blue_on_blue = "\x1B[44;34m";
825
826        assert_eq!(blue_on_blue, "".blue().on_blue().compute_style());
827    }
828
829    #[cfg_attr(feature = "no-color", ignore)]
830    #[test]
831    fn compute_style_simple_fg_bright_blue() {
832        let blue = "\x1B[94m";
833
834        assert_eq!(blue, "".bright_blue().compute_style());
835    }
836
837    #[cfg_attr(feature = "no-color", ignore)]
838    #[test]
839    fn compute_style_simple_bg_bright_blue() {
840        let on_blue = "\x1B[104m";
841
842        assert_eq!(on_blue, "".on_bright_blue().compute_style());
843    }
844
845    #[cfg_attr(feature = "no-color", ignore)]
846    #[test]
847    fn compute_style_bright_blue_on_bright_blue() {
848        let blue_on_blue = "\x1B[104;94m";
849
850        assert_eq!(
851            blue_on_blue,
852            "".bright_blue().on_bright_blue().compute_style()
853        );
854    }
855
856    #[cfg_attr(feature = "no-color", ignore)]
857    #[test]
858    fn compute_style_simple_bold() {
859        let bold = "\x1B[1m";
860
861        assert_eq!(bold, "".bold().compute_style());
862    }
863
864    #[cfg_attr(feature = "no-color", ignore)]
865    #[test]
866    fn compute_style_blue_bold() {
867        let blue_bold = "\x1B[1;34m";
868
869        assert_eq!(blue_bold, "".blue().bold().compute_style());
870    }
871
872    #[cfg_attr(feature = "no-color", ignore)]
873    #[test]
874    fn compute_style_blue_bold_on_blue() {
875        let blue_bold_on_blue = "\x1B[1;44;34m";
876
877        assert_eq!(
878            blue_bold_on_blue,
879            "".blue().bold().on_blue().compute_style()
880        );
881    }
882
883    #[test]
884    fn escape_reset_sequence_spec_should_do_nothing_on_empty_strings() {
885        let style = ColoredString::default();
886        let expected = String::new();
887
888        let output = style.escape_inner_reset_sequences();
889
890        assert_eq!(expected, output);
891    }
892
893    #[test]
894    fn escape_reset_sequence_spec_should_do_nothing_on_string_with_no_reset() {
895        let style = ColoredString {
896            input: String::from("hello world !"),
897            ..ColoredString::default()
898        };
899
900        let expected = String::from("hello world !");
901        let output = style.escape_inner_reset_sequences();
902
903        assert_eq!(expected, output);
904    }
905
906    #[cfg_attr(feature = "no-color", ignore)]
907    #[test]
908    fn escape_reset_sequence_spec_should_replace_inner_reset_sequence_with_current_style() {
909        let input = format!("start {} end", String::from("hello world !").red());
910        let style = input.blue();
911
912        let output = style.escape_inner_reset_sequences();
913        let blue = "\x1B[34m";
914        let red = "\x1B[31m";
915        let reset = "\x1B[0m";
916        let expected = format!("start {}hello world !{}{} end", red, reset, blue);
917        assert_eq!(expected, output);
918    }
919
920    #[cfg_attr(feature = "no-color", ignore)]
921    #[test]
922    fn escape_reset_sequence_spec_should_replace_multiple_inner_reset_sequences_with_current_style()
923    {
924        let italic_str = String::from("yo").italic();
925        let input = format!(
926            "start 1:{} 2:{} 3:{} end",
927            italic_str, italic_str, italic_str
928        );
929        let style = input.blue();
930
931        let output = style.escape_inner_reset_sequences();
932        let blue = "\x1B[34m";
933        let italic = "\x1B[3m";
934        let reset = "\x1B[0m";
935        let expected = format!(
936            "start 1:{}yo{}{} 2:{}yo{}{} 3:{}yo{}{} end",
937            italic, reset, blue, italic, reset, blue, italic, reset, blue
938        );
939
940        println!("first: {}\nsecond: {}", expected, output);
941
942        assert_eq!(expected, output);
943    }
944
945    #[test]
946    fn color_fn() {
947        assert_eq!("blue".blue(), "blue".color("blue"));
948    }
949
950    #[test]
951    fn on_color_fn() {
952        assert_eq!("blue".on_blue(), "blue".on_color("blue"));
953    }
954
955    #[test]
956    fn bright_color_fn() {
957        assert_eq!("blue".bright_blue(), "blue".color("bright blue"));
958    }
959
960    #[test]
961    fn on_bright_color_fn() {
962        assert_eq!("blue".on_bright_blue(), "blue".on_color("bright blue"));
963    }
964
965    #[test]
966    fn exposing_tests() {
967        #![allow(deprecated)]
968
969        let cstring = "".red();
970        assert_eq!(cstring.fgcolor(), Some(Color::Red));
971        assert_eq!(cstring.bgcolor(), None);
972
973        let cstring = cstring.clear();
974        assert_eq!(cstring.fgcolor(), None);
975        assert_eq!(cstring.bgcolor(), None);
976
977        let cstring = cstring.blue().on_bright_yellow();
978        assert_eq!(cstring.fgcolor(), Some(Color::Blue));
979        assert_eq!(cstring.bgcolor(), Some(Color::BrightYellow));
980
981        let cstring = cstring.bold().italic();
982        assert_eq!(cstring.fgcolor(), Some(Color::Blue));
983        assert_eq!(cstring.bgcolor(), Some(Color::BrightYellow));
984        assert!(cstring.style().contains(Styles::Bold));
985        assert!(cstring.style().contains(Styles::Italic));
986        assert!(!cstring.style().contains(Styles::Dimmed));
987    }
988}