termcolor/
ansi.rs

1//! ANSI escape sequence functionality for terminal color output.
2//!
3//! This module provides low-level ANSI escape sequence generation functions
4//! that can be used to write colored output to terminals that support ANSI
5//! color codes.
6
7use crate::{Color, ColorSpec};
8use std::fmt;
9use std::io;
10
11/// Writes an ANSI escape sequence corresponding to the given color specification.
12///
13/// If `reset` is true, then the reset escape sequence will be written before
14/// any color escape codes.
15///
16/// The caller must provide their own `IoWrite` to write to. Callers should
17/// prefer higher level types in this crate, such as `StandardStream` or
18/// `Buffer`.
19pub fn ansi_spec<W: io::Write>(
20    mut wtr: W,
21    spec: &ColorSpec,
22) -> io::Result<()> {
23    if spec.reset() {
24        write!(wtr, "\x1B[0m")?;
25    }
26    if spec.bold() {
27        write!(wtr, "\x1B[1m")?;
28    }
29    if spec.dimmed() {
30        write!(wtr, "\x1B[2m")?;
31    }
32    if spec.italic() {
33        write!(wtr, "\x1B[3m")?;
34    }
35    if spec.underline() {
36        write!(wtr, "\x1B[4m")?;
37    }
38    if spec.strikethrough() {
39        write!(wtr, "\x1B[9m")?;
40    }
41    if let Some(c) = spec.fg() {
42        ansi_color(&mut wtr, c, false)?;
43    }
44    if let Some(c) = spec.bg() {
45        ansi_color(&mut wtr, c, true)?;
46    }
47    if spec.intense() && spec.fg().is_some() {
48        write!(wtr, "\x1B[1m")?;
49    }
50    Ok(())
51}
52
53/// Writes an ANSI escape sequence corresponding to the given color.
54///
55/// If `bg` is true, then the color is treated as a background color.
56/// Otherwise, it's treated as a foreground color.
57///
58/// The caller must provide their own `IoWrite` to write to. Callers should
59/// prefer higher level types in this crate, such as `StandardStream` or
60/// `Buffer`.
61pub fn ansi_color<W: io::Write>(
62    mut wtr: W,
63    color: &Color,
64    bg: bool,
65) -> io::Result<()> {
66    match *color {
67        Color::Black => {
68            if bg {
69                write!(wtr, "\x1B[40m")
70            } else {
71                write!(wtr, "\x1B[30m")
72            }
73        }
74        Color::Blue => {
75            if bg {
76                write!(wtr, "\x1B[44m")
77            } else {
78                write!(wtr, "\x1B[34m")
79            }
80        }
81        Color::Green => {
82            if bg {
83                write!(wtr, "\x1B[42m")
84            } else {
85                write!(wtr, "\x1B[32m")
86            }
87        }
88        Color::Red => {
89            if bg {
90                write!(wtr, "\x1B[41m")
91            } else {
92                write!(wtr, "\x1B[31m")
93            }
94        }
95        Color::Cyan => {
96            if bg {
97                write!(wtr, "\x1B[46m")
98            } else {
99                write!(wtr, "\x1B[36m")
100            }
101        }
102        Color::Magenta => {
103            if bg {
104                write!(wtr, "\x1B[45m")
105            } else {
106                write!(wtr, "\x1B[35m")
107            }
108        }
109        Color::Yellow => {
110            if bg {
111                write!(wtr, "\x1B[43m")
112            } else {
113                write!(wtr, "\x1B[33m")
114            }
115        }
116        Color::White => {
117            if bg {
118                write!(wtr, "\x1B[47m")
119            } else {
120                write!(wtr, "\x1B[37m")
121            }
122        }
123        Color::Ansi256(n) => {
124            if bg {
125                write!(wtr, "\x1B[48;5;{n}m")
126            } else {
127                write!(wtr, "\x1B[38;5;{n}m")
128            }
129        }
130        Color::Rgb(r, g, b) => {
131            if bg {
132                write!(wtr, "\x1B[48;2;{r};{g};{b}m")
133            } else {
134                write!(wtr, "\x1B[38;2;{r};{g};{b}m")
135            }
136        }
137    }
138}
139
140/// A convenience function for creating a color specification that can be
141/// formatted to an ANSI color string.
142///
143/// This is a shorthand for creating a `ColorSpec` with the given foreground
144/// and background colors.
145pub fn ansi_color_only(fg: Option<Color>, bg: Option<Color>) -> AnsiColor {
146    AnsiColor { fg, bg }
147}
148
149/// A color specification that can be formatted to an ANSI color string.
150///
151/// This is created by the `ansi_color_only` function.
152pub struct AnsiColor {
153    fg: Option<Color>,
154    bg: Option<Color>,
155}
156
157impl fmt::Display for AnsiColor {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        let mut buf = Vec::new();
160        if let Some(ref c) = self.fg {
161            ansi_color(&mut buf, c, false).map_err(|_| fmt::Error)?;
162        }
163        if let Some(ref c) = self.bg {
164            ansi_color(&mut buf, c, true).map_err(|_| fmt::Error)?;
165        }
166        write!(f, "{}", String::from_utf8_lossy(&buf))
167    }
168}