ansi_term/
ansi.rs

1use style::{Colour, Style};
2
3use std::fmt;
4
5use write::AnyWrite;
6
7
8// ---- generating ANSI codes ----
9
10impl Style {
11
12    /// Write any ANSI codes that go *before* a piece of text. These should be
13    /// the codes to set the terminal to a different colour or font style.
14    fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
15
16        // If there are actually no styles here, then don’t write *any* codes
17        // as the prefix. An empty ANSI code may not affect the terminal
18        // output at all, but a user may just want a code-free string.
19        if self.is_plain() {
20            return Ok(());
21        }
22
23        // Write the codes’ prefix, then write numbers, separated by
24        // semicolons, for each text style we want to apply.
25        write!(f, "\x1B[")?;
26        let mut written_anything = false;
27
28        {
29            let mut write_char = |c| {
30                if written_anything { write!(f, ";")?; }
31                written_anything = true;
32                write!(f, "{}", c)?;
33                Ok(())
34            };
35
36            if self.is_bold           { write_char('1')? }
37            if self.is_dimmed         { write_char('2')? }
38            if self.is_italic         { write_char('3')? }
39            if self.is_underline      { write_char('4')? }
40            if self.is_blink          { write_char('5')? }
41            if self.is_reverse        { write_char('7')? }
42            if self.is_hidden         { write_char('8')? }
43            if self.is_strikethrough  { write_char('9')? }
44        }
45
46        // The foreground and background colours, if specified, need to be
47        // handled specially because the number codes are more complicated.
48        // (see `write_background_code` and `write_foreground_code`)
49        if let Some(bg) = self.background {
50            if written_anything { write!(f, ";")?; }
51            written_anything = true;
52            bg.write_background_code(f)?;
53        }
54
55        if let Some(fg) = self.foreground {
56            if written_anything { write!(f, ";")?; }
57            fg.write_foreground_code(f)?;
58        }
59
60        // All the codes end with an `m`, because reasons.
61        write!(f, "m")?;
62
63        Ok(())
64    }
65
66    /// Write any ANSI codes that go *after* a piece of text. These should be
67    /// the codes to *reset* the terminal back to its normal colour and style.
68    fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
69        if self.is_plain() {
70            Ok(())
71        }
72        else {
73            write!(f, "{}", RESET)
74        }
75    }
76}
77
78
79/// The code to send to reset all styles and return to `Style::default()`.
80pub static RESET: &str = "\x1B[0m";
81
82
83
84impl Colour {
85    fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
86        match *self {
87            Colour::Black      => write!(f, "30"),
88            Colour::Red        => write!(f, "31"),
89            Colour::Green      => write!(f, "32"),
90            Colour::Yellow     => write!(f, "33"),
91            Colour::Blue       => write!(f, "34"),
92            Colour::Purple     => write!(f, "35"),
93            Colour::Cyan       => write!(f, "36"),
94            Colour::White      => write!(f, "37"),
95            Colour::Fixed(num) => write!(f, "38;5;{}", &num),
96            Colour::RGB(r,g,b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
97        }
98    }
99
100    fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
101        match *self {
102            Colour::Black      => write!(f, "40"),
103            Colour::Red        => write!(f, "41"),
104            Colour::Green      => write!(f, "42"),
105            Colour::Yellow     => write!(f, "43"),
106            Colour::Blue       => write!(f, "44"),
107            Colour::Purple     => write!(f, "45"),
108            Colour::Cyan       => write!(f, "46"),
109            Colour::White      => write!(f, "47"),
110            Colour::Fixed(num) => write!(f, "48;5;{}", &num),
111            Colour::RGB(r,g,b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
112        }
113    }
114}
115
116
117/// Like `ANSIString`, but only displays the style prefix.
118#[derive(Clone, Copy, Debug)]
119pub struct Prefix(Style);
120
121/// Like `ANSIString`, but only displays the difference between two
122/// styles.
123#[derive(Clone, Copy, Debug)]
124pub struct Infix(Style, Style);
125
126/// Like `ANSIString`, but only displays the style suffix.
127#[derive(Clone, Copy, Debug)]
128pub struct Suffix(Style);
129
130
131impl Style {
132
133    /// The prefix for this style.
134    pub fn prefix(self) -> Prefix {
135        Prefix(self)
136    }
137
138    /// The infix between this style and another.
139    pub fn infix(self, other: Style) -> Infix {
140        Infix(self, other)
141    }
142
143    /// The suffix for this style.
144    pub fn suffix(self) -> Suffix {
145        Suffix(self)
146    }
147}
148
149
150impl Colour {
151
152    /// The prefix for this colour.
153    pub fn prefix(self) -> Prefix {
154        Prefix(self.normal())
155    }
156
157    /// The infix between this colour and another.
158    pub fn infix(self, other: Colour) -> Infix {
159        Infix(self.normal(), other.normal())
160    }
161
162    /// The suffix for this colour.
163    pub fn suffix(self) -> Suffix {
164        Suffix(self.normal())
165    }
166}
167
168
169impl fmt::Display for Prefix {
170    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171        let f: &mut fmt::Write = f;
172        self.0.write_prefix(f)
173    }
174}
175
176
177impl fmt::Display for Infix {
178    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179        use difference::Difference;
180
181        match Difference::between(&self.0, &self.1) {
182            Difference::ExtraStyles(style) => {
183                let f: &mut fmt::Write = f;
184                style.write_prefix(f)
185            },
186            Difference::Reset => {
187                let f: &mut fmt::Write = f;
188                write!(f, "{}{}", RESET, self.0.prefix())
189            },
190            Difference::NoDifference => {
191                Ok(())   // nothing to write
192            },
193        }
194    }
195}
196
197
198impl fmt::Display for Suffix {
199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200        let f: &mut fmt::Write = f;
201        self.0.write_suffix(f)
202    }
203}
204
205
206
207#[cfg(test)]
208mod test {
209    use style::Style;
210    use style::Colour::*;
211
212    macro_rules! test {
213        ($name: ident: $style: expr; $input: expr => $result: expr) => {
214            #[test]
215            fn $name() {
216                assert_eq!($style.paint($input).to_string(), $result.to_string());
217
218                let mut v = Vec::new();
219                $style.paint($input.as_bytes()).write_to(&mut v).unwrap();
220                assert_eq!(v.as_slice(), $result.as_bytes());
221            }
222        };
223    }
224
225    test!(plain:                 Style::default();                  "text/plain" => "text/plain");
226    test!(red:                   Red;                               "hi" => "\x1B[31mhi\x1B[0m");
227    test!(black:                 Black.normal();                    "hi" => "\x1B[30mhi\x1B[0m");
228    test!(yellow_bold:           Yellow.bold();                     "hi" => "\x1B[1;33mhi\x1B[0m");
229    test!(yellow_bold_2:         Yellow.normal().bold();            "hi" => "\x1B[1;33mhi\x1B[0m");
230    test!(blue_underline:        Blue.underline();                  "hi" => "\x1B[4;34mhi\x1B[0m");
231    test!(green_bold_ul:         Green.bold().underline();          "hi" => "\x1B[1;4;32mhi\x1B[0m");
232    test!(green_bold_ul_2:       Green.underline().bold();          "hi" => "\x1B[1;4;32mhi\x1B[0m");
233    test!(purple_on_white:       Purple.on(White);                  "hi" => "\x1B[47;35mhi\x1B[0m");
234    test!(purple_on_white_2:     Purple.normal().on(White);         "hi" => "\x1B[47;35mhi\x1B[0m");
235    test!(yellow_on_blue:        Style::new().on(Blue).fg(Yellow);  "hi" => "\x1B[44;33mhi\x1B[0m");
236    test!(yellow_on_blue_2:      Cyan.on(Blue).fg(Yellow);          "hi" => "\x1B[44;33mhi\x1B[0m");
237    test!(cyan_bold_on_white:    Cyan.bold().on(White);             "hi" => "\x1B[1;47;36mhi\x1B[0m");
238    test!(cyan_ul_on_white:      Cyan.underline().on(White);        "hi" => "\x1B[4;47;36mhi\x1B[0m");
239    test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
240    test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
241    test!(fixed:                 Fixed(100);                        "hi" => "\x1B[38;5;100mhi\x1B[0m");
242    test!(fixed_on_purple:       Fixed(100).on(Purple);             "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
243    test!(fixed_on_fixed:        Fixed(100).on(Fixed(200));         "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
244    test!(rgb:                   RGB(70,130,180);                   "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
245    test!(rgb_on_blue:           RGB(70,130,180).on(Blue);          "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
246    test!(blue_on_rgb:           Blue.on(RGB(70,130,180));          "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
247    test!(rgb_on_rgb:            RGB(70,130,180).on(RGB(5,10,15));  "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
248    test!(bold:                  Style::new().bold();               "hi" => "\x1B[1mhi\x1B[0m");
249    test!(underline:             Style::new().underline();          "hi" => "\x1B[4mhi\x1B[0m");
250    test!(bunderline:            Style::new().bold().underline();   "hi" => "\x1B[1;4mhi\x1B[0m");
251    test!(dimmed:                Style::new().dimmed();             "hi" => "\x1B[2mhi\x1B[0m");
252    test!(italic:                Style::new().italic();             "hi" => "\x1B[3mhi\x1B[0m");
253    test!(blink:                 Style::new().blink();              "hi" => "\x1B[5mhi\x1B[0m");
254    test!(reverse:               Style::new().reverse();            "hi" => "\x1B[7mhi\x1B[0m");
255    test!(hidden:                Style::new().hidden();             "hi" => "\x1B[8mhi\x1B[0m");
256    test!(stricken:              Style::new().strikethrough();      "hi" => "\x1B[9mhi\x1B[0m");
257
258}