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