Coverage Report

Created: 2025-08-26 07:09

/rust/registry/src/index.crates.io-6f17d22bba15001f/nu-ansi-term-0.46.0/src/ansi.rs
Line
Count
Source (jump to first uncovered line)
1
#![allow(missing_docs)]
2
use crate::style::{Color, Style};
3
use crate::write::AnyWrite;
4
use std::fmt;
5
6
impl Style {
7
    /// Write any bytes that go *before* a piece of text to the given writer.
8
0
    fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
9
0
        // If there are actually no styles here, then don’t write *any* codes
10
0
        // as the prefix. An empty ANSI code may not affect the terminal
11
0
        // output at all, but a user may just want a code-free string.
12
0
        if self.is_plain() {
13
0
            return Ok(());
14
0
        }
15
0
16
0
        // Write the codes’ prefix, then write numbers, separated by
17
0
        // semicolons, for each text style we want to apply.
18
0
        write!(f, "\x1B[")?;
19
0
        let mut written_anything = false;
20
0
21
0
        {
22
0
            let mut write_char = |c| {
23
0
                if written_anything {
24
0
                    write!(f, ";")?;
25
0
                }
26
0
                written_anything = true;
27
0
                write!(f, "{}", c)?;
28
0
                Ok(())
29
0
            };
30
31
0
            if self.is_bold {
32
0
                write_char('1')?
33
0
            }
34
0
            if self.is_dimmed {
35
0
                write_char('2')?
36
0
            }
37
0
            if self.is_italic {
38
0
                write_char('3')?
39
0
            }
40
0
            if self.is_underline {
41
0
                write_char('4')?
42
0
            }
43
0
            if self.is_blink {
44
0
                write_char('5')?
45
0
            }
46
0
            if self.is_reverse {
47
0
                write_char('7')?
48
0
            }
49
0
            if self.is_hidden {
50
0
                write_char('8')?
51
0
            }
52
0
            if self.is_strikethrough {
53
0
                write_char('9')?
54
0
            }
55
        }
56
57
        // The foreground and background colors, if specified, need to be
58
        // handled specially because the number codes are more complicated.
59
        // (see `write_background_code` and `write_foreground_code`)
60
0
        if let Some(bg) = self.background {
61
0
            if written_anything {
62
0
                write!(f, ";")?;
63
0
            }
64
0
            written_anything = true;
65
0
            bg.write_background_code(f)?;
66
0
        }
67
68
0
        if let Some(fg) = self.foreground {
69
0
            if written_anything {
70
0
                write!(f, ";")?;
71
0
            }
72
0
            fg.write_foreground_code(f)?;
73
0
        }
74
75
        // All the codes end with an `m`, because reasons.
76
0
        write!(f, "m")?;
77
78
0
        Ok(())
79
0
    }
80
81
    /// Write any bytes that go *after* a piece of text to the given writer.
82
0
    fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
83
0
        if self.is_plain() {
84
0
            Ok(())
85
        } else {
86
0
            write!(f, "{}", RESET)
87
        }
88
0
    }
89
}
90
91
/// The code to send to reset all styles and return to `Style::default()`.
92
pub static RESET: &str = "\x1B[0m";
93
94
impl Color {
95
0
    fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
96
0
        match self {
97
0
            Color::Black => write!(f, "30"),
98
0
            Color::Red => write!(f, "31"),
99
0
            Color::Green => write!(f, "32"),
100
0
            Color::Yellow => write!(f, "33"),
101
0
            Color::Blue => write!(f, "34"),
102
0
            Color::Purple => write!(f, "35"),
103
0
            Color::Magenta => write!(f, "35"),
104
0
            Color::Cyan => write!(f, "36"),
105
0
            Color::White => write!(f, "37"),
106
0
            Color::Fixed(num) => write!(f, "38;5;{}", num),
107
0
            Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", r, g, b),
108
0
            Color::Default => write!(f, "39"),
109
0
            Color::DarkGray => write!(f, "90"),
110
0
            Color::LightRed => write!(f, "91"),
111
0
            Color::LightGreen => write!(f, "92"),
112
0
            Color::LightYellow => write!(f, "93"),
113
0
            Color::LightBlue => write!(f, "94"),
114
0
            Color::LightPurple => write!(f, "95"),
115
0
            Color::LightMagenta => write!(f, "95"),
116
0
            Color::LightCyan => write!(f, "96"),
117
0
            Color::LightGray => write!(f, "97"),
118
        }
119
0
    }
120
121
0
    fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
122
0
        match self {
123
0
            Color::Black => write!(f, "40"),
124
0
            Color::Red => write!(f, "41"),
125
0
            Color::Green => write!(f, "42"),
126
0
            Color::Yellow => write!(f, "43"),
127
0
            Color::Blue => write!(f, "44"),
128
0
            Color::Purple => write!(f, "45"),
129
0
            Color::Magenta => write!(f, "45"),
130
0
            Color::Cyan => write!(f, "46"),
131
0
            Color::White => write!(f, "47"),
132
0
            Color::Fixed(num) => write!(f, "48;5;{}", num),
133
0
            Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", r, g, b),
134
0
            Color::Default => write!(f, "49"),
135
0
            Color::DarkGray => write!(f, "100"),
136
0
            Color::LightRed => write!(f, "101"),
137
0
            Color::LightGreen => write!(f, "102"),
138
0
            Color::LightYellow => write!(f, "103"),
139
0
            Color::LightBlue => write!(f, "104"),
140
0
            Color::LightPurple => write!(f, "105"),
141
0
            Color::LightMagenta => write!(f, "105"),
142
0
            Color::LightCyan => write!(f, "106"),
143
0
            Color::LightGray => write!(f, "107"),
144
        }
145
0
    }
146
}
147
148
/// Like `AnsiString`, but only displays the style prefix.
149
///
150
/// This type implements the `Display` trait, meaning it can be written to a
151
/// `std::fmt` formatting without doing any extra allocation, and written to a
152
/// string with the `.to_string()` method. For examples, see
153
/// [`Style::prefix`](struct.Style.html#method.prefix).
154
#[derive(Clone, Copy, Debug)]
155
pub struct Prefix(Style);
156
157
/// Like `AnsiString`, but only displays the difference between two
158
/// styles.
159
///
160
/// This type implements the `Display` trait, meaning it can be written to a
161
/// `std::fmt` formatting without doing any extra allocation, and written to a
162
/// string with the `.to_string()` method. For examples, see
163
/// [`Style::infix`](struct.Style.html#method.infix).
164
#[derive(Clone, Copy, Debug)]
165
pub struct Infix(Style, Style);
166
167
/// Like `AnsiString`, but only displays the style suffix.
168
///
169
/// This type implements the `Display` trait, meaning it can be written to a
170
/// `std::fmt` formatting without doing any extra allocation, and written to a
171
/// string with the `.to_string()` method. For examples, see
172
/// [`Style::suffix`](struct.Style.html#method.suffix).
173
#[derive(Clone, Copy, Debug)]
174
pub struct Suffix(Style);
175
176
impl Style {
177
    /// The prefix bytes for this style. These are the bytes that tell the
178
    /// terminal to use a different color or font style.
179
    ///
180
    /// # Examples
181
    ///
182
    /// ```
183
    /// use nu_ansi_term::{Style, Color::Blue};
184
    ///
185
    /// let style = Style::default().bold();
186
    /// assert_eq!("\x1b[1m",
187
    ///            style.prefix().to_string());
188
    ///
189
    /// let style = Blue.bold();
190
    /// assert_eq!("\x1b[1;34m",
191
    ///            style.prefix().to_string());
192
    ///
193
    /// let style = Style::default();
194
    /// assert_eq!("",
195
    ///            style.prefix().to_string());
196
    /// ```
197
0
    pub fn prefix(self) -> Prefix {
198
0
        Prefix(self)
199
0
    }
200
201
    /// The infix bytes between this style and `next` style. These are the bytes
202
    /// that tell the terminal to change the style to `next`. These may include
203
    /// a reset followed by the next color and style, depending on the two styles.
204
    ///
205
    /// # Examples
206
    ///
207
    /// ```
208
    /// use nu_ansi_term::{Style, Color::Green};
209
    ///
210
    /// let style = Style::default().bold();
211
    /// assert_eq!("\x1b[32m",
212
    ///            style.infix(Green.bold()).to_string());
213
    ///
214
    /// let style = Green.normal();
215
    /// assert_eq!("\x1b[1m",
216
    ///            style.infix(Green.bold()).to_string());
217
    ///
218
    /// let style = Style::default();
219
    /// assert_eq!("",
220
    ///            style.infix(style).to_string());
221
    /// ```
222
0
    pub fn infix(self, next: Style) -> Infix {
223
0
        Infix(self, next)
224
0
    }
225
226
    /// The suffix for this style. These are the bytes that tell the terminal
227
    /// to reset back to its normal color and font style.
228
    ///
229
    /// # Examples
230
    ///
231
    /// ```
232
    /// use nu_ansi_term::{Style, Color::Green};
233
    ///
234
    /// let style = Style::default().bold();
235
    /// assert_eq!("\x1b[0m",
236
    ///            style.suffix().to_string());
237
    ///
238
    /// let style = Green.normal().bold();
239
    /// assert_eq!("\x1b[0m",
240
    ///            style.suffix().to_string());
241
    ///
242
    /// let style = Style::default();
243
    /// assert_eq!("",
244
    ///            style.suffix().to_string());
245
    /// ```
246
0
    pub fn suffix(self) -> Suffix {
247
0
        Suffix(self)
248
0
    }
249
}
250
251
impl Color {
252
    /// The prefix bytes for this color as a `Style`. These are the bytes
253
    /// that tell the terminal to use a different color or font style.
254
    ///
255
    /// See also [`Style::prefix`](struct.Style.html#method.prefix).
256
    ///
257
    /// # Examples
258
    ///
259
    /// ```
260
    /// use nu_ansi_term::Color::Green;
261
    ///
262
    /// assert_eq!("\x1b[0m",
263
    ///            Green.suffix().to_string());
264
    /// ```
265
0
    pub fn prefix(self) -> Prefix {
266
0
        Prefix(self.normal())
267
0
    }
268
269
    /// The infix bytes between this color and `next` color. These are the bytes
270
    /// that tell the terminal to use the `next` color, or to do nothing if
271
    /// the two colors are equal.
272
    ///
273
    /// See also [`Style::infix`](struct.Style.html#method.infix).
274
    ///
275
    /// # Examples
276
    ///
277
    /// ```
278
    /// use nu_ansi_term::Color::{Red, Yellow};
279
    ///
280
    /// assert_eq!("\x1b[33m",
281
    ///            Red.infix(Yellow).to_string());
282
    /// ```
283
0
    pub fn infix(self, next: Color) -> Infix {
284
0
        Infix(self.normal(), next.normal())
285
0
    }
286
287
    /// The suffix for this color as a `Style`. These are the bytes that
288
    /// tell the terminal to reset back to its normal color and font style.
289
    ///
290
    /// See also [`Style::suffix`](struct.Style.html#method.suffix).
291
    ///
292
    /// # Examples
293
    ///
294
    /// ```
295
    /// use nu_ansi_term::Color::Purple;
296
    ///
297
    /// assert_eq!("\x1b[0m",
298
    ///            Purple.suffix().to_string());
299
    /// ```
300
0
    pub fn suffix(self) -> Suffix {
301
0
        Suffix(self.normal())
302
0
    }
303
}
304
305
impl fmt::Display for Prefix {
306
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
307
0
        let f: &mut dyn fmt::Write = f;
308
0
        self.0.write_prefix(f)
309
0
    }
310
}
311
312
impl fmt::Display for Infix {
313
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
314
        use crate::difference::Difference;
315
316
0
        match Difference::between(&self.0, &self.1) {
317
0
            Difference::ExtraStyles(style) => {
318
0
                let f: &mut dyn fmt::Write = f;
319
0
                style.write_prefix(f)
320
            }
321
            Difference::Reset => {
322
0
                let f: &mut dyn fmt::Write = f;
323
0
                write!(f, "{}{}", RESET, self.1.prefix())
324
            }
325
            Difference::Empty => {
326
0
                Ok(()) // nothing to write
327
            }
328
        }
329
0
    }
330
}
331
332
impl fmt::Display for Suffix {
333
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
334
0
        let f: &mut dyn fmt::Write = f;
335
0
        self.0.write_suffix(f)
336
0
    }
337
}
338
339
#[cfg(test)]
340
mod test {
341
    use crate::style::Color::*;
342
    use crate::style::Style;
343
344
    macro_rules! test {
345
        ($name: ident: $style: expr; $input: expr => $result: expr) => {
346
            #[test]
347
            fn $name() {
348
                assert_eq!($style.paint($input).to_string(), $result.to_string());
349
350
                let mut v = Vec::new();
351
                $style.paint($input.as_bytes()).write_to(&mut v).unwrap();
352
                assert_eq!(v.as_slice(), $result.as_bytes());
353
            }
354
        };
355
    }
356
357
    test!(plain:                 Style::default();                  "text/plain" => "text/plain");
358
    test!(red:                   Red;                               "hi" => "\x1B[31mhi\x1B[0m");
359
    test!(black:                 Black.normal();                    "hi" => "\x1B[30mhi\x1B[0m");
360
    test!(yellow_bold:           Yellow.bold();                     "hi" => "\x1B[1;33mhi\x1B[0m");
361
    test!(yellow_bold_2:         Yellow.normal().bold();            "hi" => "\x1B[1;33mhi\x1B[0m");
362
    test!(blue_underline:        Blue.underline();                  "hi" => "\x1B[4;34mhi\x1B[0m");
363
    test!(green_bold_ul:         Green.bold().underline();          "hi" => "\x1B[1;4;32mhi\x1B[0m");
364
    test!(green_bold_ul_2:       Green.underline().bold();          "hi" => "\x1B[1;4;32mhi\x1B[0m");
365
    test!(purple_on_white:       Purple.on(White);                  "hi" => "\x1B[47;35mhi\x1B[0m");
366
    test!(purple_on_white_2:     Purple.normal().on(White);         "hi" => "\x1B[47;35mhi\x1B[0m");
367
    test!(yellow_on_blue:        Style::new().on(Blue).fg(Yellow);  "hi" => "\x1B[44;33mhi\x1B[0m");
368
    test!(magenta_on_white:      Magenta.on(White);                  "hi" => "\x1B[47;35mhi\x1B[0m");
369
    test!(magenta_on_white_2:    Magenta.normal().on(White);         "hi" => "\x1B[47;35mhi\x1B[0m");
370
    test!(yellow_on_blue_2:      Cyan.on(Blue).fg(Yellow);          "hi" => "\x1B[44;33mhi\x1B[0m");
371
    test!(cyan_bold_on_white:    Cyan.bold().on(White);             "hi" => "\x1B[1;47;36mhi\x1B[0m");
372
    test!(cyan_ul_on_white:      Cyan.underline().on(White);        "hi" => "\x1B[4;47;36mhi\x1B[0m");
373
    test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
374
    test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
375
    test!(fixed:                 Fixed(100);                        "hi" => "\x1B[38;5;100mhi\x1B[0m");
376
    test!(fixed_on_purple:       Fixed(100).on(Purple);             "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
377
    test!(fixed_on_fixed:        Fixed(100).on(Fixed(200));         "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
378
    test!(rgb:                   Rgb(70,130,180);                   "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
379
    test!(rgb_on_blue:           Rgb(70,130,180).on(Blue);          "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
380
    test!(blue_on_rgb:           Blue.on(Rgb(70,130,180));          "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
381
    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");
382
    test!(bold:                  Style::new().bold();               "hi" => "\x1B[1mhi\x1B[0m");
383
    test!(underline:             Style::new().underline();          "hi" => "\x1B[4mhi\x1B[0m");
384
    test!(bunderline:            Style::new().bold().underline();   "hi" => "\x1B[1;4mhi\x1B[0m");
385
    test!(dimmed:                Style::new().dimmed();             "hi" => "\x1B[2mhi\x1B[0m");
386
    test!(italic:                Style::new().italic();             "hi" => "\x1B[3mhi\x1B[0m");
387
    test!(blink:                 Style::new().blink();              "hi" => "\x1B[5mhi\x1B[0m");
388
    test!(reverse:               Style::new().reverse();            "hi" => "\x1B[7mhi\x1B[0m");
389
    test!(hidden:                Style::new().hidden();             "hi" => "\x1B[8mhi\x1B[0m");
390
    test!(stricken:              Style::new().strikethrough();      "hi" => "\x1B[9mhi\x1B[0m");
391
    test!(lr_on_lr:              LightRed.on(LightRed);             "hi" => "\x1B[101;91mhi\x1B[0m");
392
393
    #[test]
394
    fn test_infix() {
395
        assert_eq!(
396
            Style::new().dimmed().infix(Style::new()).to_string(),
397
            "\x1B[0m"
398
        );
399
        assert_eq!(
400
            White.dimmed().infix(White.normal()).to_string(),
401
            "\x1B[0m\x1B[37m"
402
        );
403
        assert_eq!(White.normal().infix(White.bold()).to_string(), "\x1B[1m");
404
        assert_eq!(White.normal().infix(Blue.normal()).to_string(), "\x1B[34m");
405
        assert_eq!(Blue.bold().infix(Blue.bold()).to_string(), "");
406
    }
407
}