afetch_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#[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/// The trait that enables something to be given color.
154///
155/// You can use `colored` effectively simply by importing this trait
156/// and then using its methods on `String` and `&str`.
157#[allow(missing_docs)]
158pub trait Colorize {
159    // Font Colors
160    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    // Background Colors
303    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    // Styles
432    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    /// Get the current background color applied.
448    ///
449    /// ```rust
450    /// # use colored::*;
451    /// let cstr = "".blue();
452    /// assert_eq!(cstr.fgcolor(), Some(Color::Blue));
453    /// let cstr = cstr.clear();
454    /// assert_eq!(cstr.fgcolor(), None);
455    /// ```
456    #[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    /// Get the current background color applied.
462    ///
463    /// ```rust
464    /// # use colored::*;
465    /// let cstr = "".on_blue();
466    /// assert_eq!(cstr.bgcolor(), Some(Color::Blue));
467    /// let cstr = cstr.clear();
468    /// assert_eq!(cstr.bgcolor(), None);
469    /// ```
470    #[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    /// Get the current [`Style`] which can be check if it contains a [`Styles`].
476    ///
477    /// ```rust
478    /// # use colored::*;
479    /// let colored = "".bold().italic();
480    /// assert_eq!(colored.style().contains(Styles::Bold), true);
481    /// assert_eq!(colored.style().contains(Styles::Italic), true);
482    /// assert_eq!(colored.style().contains(Styles::Dimmed), false);
483    /// ```
484    #[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    /// Clears foreground coloring on this `ColoredString`, meaning that it
490    /// will be printed with the default terminal text color.
491    pub fn clear_fgcolor(&mut self) {
492        self.fgcolor = None;
493    }
494
495    /// Gets rid of this `ColoredString`'s background.
496    pub fn clear_bgcolor(&mut self) {
497        self.bgcolor = None;
498    }
499
500    /// Clears any special styling and sets it back to the default (plain,
501    /// maybe colored, text).
502    pub fn clear_style(&mut self) {
503        self.style = Style::default();
504    }
505
506    /// Checks if the colored string has no color or styling.
507    ///
508    /// ```rust
509    /// # use colored::*;
510    /// let cstr = "".red();
511    /// assert_eq!(cstr.is_plain(), false);
512    /// let cstr = cstr.clear();
513    /// assert_eq!(cstr.is_plain(), true);
514    /// ```
515    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        // TODO: BoyScoutRule
569        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            // shift the offset to the end of the reset sequence and take in account
585            // the number of matches we have escaped (which shift the index to insert)
586            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        // XXX: see tests. Useful when nesting colored strings
748        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        // respect the formatting. Escape sequence add some padding so >= 40
771        assert!(format!("{:40}", "".blue()).len() >= 40);
772        // both should be truncated to 1 char before coloring
773        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}