Skip to main content

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