crossterm/style/
stylize.rs

1use std::fmt::Display;
2
3use super::{style, Attribute, Color, ContentStyle, StyledContent};
4
5macro_rules! stylize_method {
6    ($method_name:ident Attribute::$attribute:ident) => {
7        calculated_docs! {
8            #[doc = concat!(
9                "Applies the [`",
10                stringify!($attribute),
11                "`](Attribute::",
12                stringify!($attribute),
13                ") attribute to the text.",
14            )]
15            fn $method_name(self) -> Self::Styled {
16                self.attribute(Attribute::$attribute)
17            }
18        }
19    };
20    ($method_name_fg:ident, $method_name_bg:ident, $method_name_ul:ident Color::$color:ident) => {
21        calculated_docs! {
22            #[doc = concat!(
23                "Sets the foreground color to [`",
24                stringify!($color),
25                "`](Color::",
26                stringify!($color),
27                ")."
28            )]
29            fn $method_name_fg(self) -> Self::Styled {
30                self.with(Color::$color)
31            }
32
33            #[doc = concat!(
34                "Sets the background color to [`",
35                stringify!($color),
36                "`](Color::",
37                stringify!($color),
38                ")."
39            )]
40            fn $method_name_bg(self) -> Self::Styled {
41                self.on(Color::$color)
42            }
43
44            #[doc = concat!(
45                "Sets the underline color to [`",
46                stringify!($color),
47                "`](Color::",
48                stringify!($color),
49                ")."
50            )]
51            fn $method_name_ul(self) -> Self::Styled {
52                self.underline(Color::$color)
53            }
54        }
55    };
56}
57
58/// Provides a set of methods to set attributes and colors.
59///
60/// # Examples
61///
62/// ```no_run
63/// use crossterm::style::Stylize;
64///
65/// println!("{}", "Bold text".bold());
66/// println!("{}", "Underlined text".underlined());
67/// println!("{}", "Negative text".negative());
68/// println!("{}", "Red on blue".red().on_blue());
69/// ```
70pub trait Stylize: Sized {
71    /// This type with styles applied.
72    type Styled: AsRef<ContentStyle> + AsMut<ContentStyle>;
73
74    /// Styles this type.
75    fn stylize(self) -> Self::Styled;
76
77    /// Sets the foreground color.
78    fn with(self, color: Color) -> Self::Styled {
79        let mut styled = self.stylize();
80        styled.as_mut().foreground_color = Some(color);
81        styled
82    }
83
84    /// Sets the background color.
85    fn on(self, color: Color) -> Self::Styled {
86        let mut styled = self.stylize();
87        styled.as_mut().background_color = Some(color);
88        styled
89    }
90
91    /// Sets the underline color.
92    fn underline(self, color: Color) -> Self::Styled {
93        let mut styled = self.stylize();
94        styled.as_mut().underline_color = Some(color);
95        styled
96    }
97
98    /// Styles the content with the attribute.
99    fn attribute(self, attr: Attribute) -> Self::Styled {
100        let mut styled = self.stylize();
101        styled.as_mut().attributes.set(attr);
102        styled
103    }
104
105    stylize_method!(reset Attribute::Reset);
106    stylize_method!(bold Attribute::Bold);
107    stylize_method!(underlined Attribute::Underlined);
108    stylize_method!(reverse Attribute::Reverse);
109    stylize_method!(dim Attribute::Dim);
110    stylize_method!(italic Attribute::Italic);
111    stylize_method!(negative Attribute::Reverse);
112    stylize_method!(slow_blink Attribute::SlowBlink);
113    stylize_method!(rapid_blink Attribute::RapidBlink);
114    stylize_method!(hidden Attribute::Hidden);
115    stylize_method!(crossed_out Attribute::CrossedOut);
116
117    stylize_method!(black, on_black, underline_black Color::Black);
118    stylize_method!(dark_grey, on_dark_grey, underline_dark_grey Color::DarkGrey);
119    stylize_method!(red, on_red, underline_red Color::Red);
120    stylize_method!(dark_red, on_dark_red, underline_dark_red Color::DarkRed);
121    stylize_method!(green, on_green, underline_green Color::Green);
122    stylize_method!(dark_green, on_dark_green, underline_dark_green Color::DarkGreen);
123    stylize_method!(yellow, on_yellow, underline_yellow Color::Yellow);
124    stylize_method!(dark_yellow, on_dark_yellow, underline_dark_yellow Color::DarkYellow);
125    stylize_method!(blue, on_blue, underline_blue Color::Blue);
126    stylize_method!(dark_blue, on_dark_blue, underline_dark_blue Color::DarkBlue);
127    stylize_method!(magenta, on_magenta, underline_magenta Color::Magenta);
128    stylize_method!(dark_magenta, on_dark_magenta, underline_dark_magenta Color::DarkMagenta);
129    stylize_method!(cyan, on_cyan, underline_cyan Color::Cyan);
130    stylize_method!(dark_cyan, on_dark_cyan, underline_dark_cyan Color::DarkCyan);
131    stylize_method!(white, on_white, underline_white Color::White);
132    stylize_method!(grey, on_grey, underline_grey Color::Grey);
133}
134
135macro_rules! impl_stylize_for_display {
136    ($($t:ty),*) => { $(
137        impl Stylize for $t {
138            type Styled = StyledContent<Self>;
139            #[inline]
140            fn stylize(self) -> Self::Styled {
141                style(self)
142            }
143        }
144    )* }
145}
146impl_stylize_for_display!(String, char, &str);
147
148impl Stylize for ContentStyle {
149    type Styled = Self;
150    #[inline]
151    fn stylize(self) -> Self::Styled {
152        self
153    }
154}
155impl<D: Display> Stylize for StyledContent<D> {
156    type Styled = StyledContent<D>;
157    fn stylize(self) -> Self::Styled {
158        self
159    }
160}
161
162// Workaround for https://github.com/rust-lang/rust/issues/78835
163macro_rules! calculated_docs {
164    ($(#[doc = $doc:expr] $item:item)*) => { $(#[doc = $doc] $item)* };
165}
166// Remove once https://github.com/rust-lang/rust-clippy/issues/7106 stabilizes.
167#[allow(clippy::single_component_path_imports)]
168#[allow(clippy::useless_attribute)]
169use calculated_docs;
170
171#[cfg(test)]
172mod tests {
173    use super::super::{Attribute, Color, ContentStyle, Stylize};
174
175    #[test]
176    fn set_fg_bg_add_attr() {
177        let style = ContentStyle::new()
178            .with(Color::Blue)
179            .on(Color::Red)
180            .attribute(Attribute::Bold);
181
182        assert_eq!(style.foreground_color, Some(Color::Blue));
183        assert_eq!(style.background_color, Some(Color::Red));
184        assert!(style.attributes.has(Attribute::Bold));
185
186        let mut styled_content = style.apply("test");
187
188        styled_content = styled_content
189            .with(Color::Green)
190            .on(Color::Magenta)
191            .attribute(Attribute::NoItalic);
192
193        let style = styled_content.style();
194
195        assert_eq!(style.foreground_color, Some(Color::Green));
196        assert_eq!(style.background_color, Some(Color::Magenta));
197        assert!(style.attributes.has(Attribute::Bold));
198        assert!(style.attributes.has(Attribute::NoItalic));
199    }
200}