env_logger/fmt/
style.rs

1use std::borrow::Cow;
2use std::cell::RefCell;
3use std::fmt;
4use std::rc::Rc;
5
6use super::Buffer;
7
8/// A set of styles to apply to the terminal output.
9///
10/// Call [`Formatter::style`] to get a `Style` and use the builder methods to
11/// set styling properties, like [color] and [weight].
12/// To print a value using the style, wrap it in a call to [`value`] when the log
13/// record is formatted.
14///
15/// # Examples
16///
17/// Create a bold, red colored style and use it to print the log level:
18///
19/// ```
20/// use std::io::Write;
21/// use env_logger::fmt::Color;
22///
23/// let mut builder = env_logger::Builder::new();
24///
25/// builder.format(|buf, record| {
26///     let mut level_style = buf.style();
27///
28///     level_style.set_color(Color::Red).set_bold(true);
29///
30///     writeln!(buf, "{}: {}",
31///         level_style.value(record.level()),
32///         record.args())
33/// });
34/// ```
35///
36/// Styles can be re-used to output multiple values:
37///
38/// ```
39/// use std::io::Write;
40/// use env_logger::fmt::Color;
41///
42/// let mut builder = env_logger::Builder::new();
43///
44/// builder.format(|buf, record| {
45///     let mut bold = buf.style();
46///
47///     bold.set_bold(true);
48///
49///     writeln!(buf, "{}: {} {}",
50///         bold.value(record.level()),
51///         bold.value("some bold text"),
52///         record.args())
53/// });
54/// ```
55///
56/// [`Formatter::style`]: struct.Formatter.html#method.style
57/// [color]: #method.set_color
58/// [weight]: #method.set_bold
59/// [`value`]: #method.value
60#[derive(Clone)]
61pub struct Style {
62    pub(in crate::fmt) buf: Rc<RefCell<Buffer>>,
63    pub(in crate::fmt) spec: termcolor::ColorSpec,
64}
65
66impl Style {
67    /// Set the text color.
68    ///
69    /// # Examples
70    ///
71    /// Create a style with red text:
72    ///
73    /// ```
74    /// use std::io::Write;
75    /// use env_logger::fmt::Color;
76    ///
77    /// let mut builder = env_logger::Builder::new();
78    ///
79    /// builder.format(|buf, record| {
80    ///     let mut style = buf.style();
81    ///
82    ///     style.set_color(Color::Red);
83    ///
84    ///     writeln!(buf, "{}", style.value(record.args()))
85    /// });
86    /// ```
87    pub fn set_color(&mut self, color: Color) -> &mut Style {
88        self.spec.set_fg(Some(color.into_termcolor()));
89        self
90    }
91
92    /// Set the text weight.
93    ///
94    /// If `yes` is true then text will be written in bold.
95    /// If `yes` is false then text will be written in the default weight.
96    ///
97    /// # Examples
98    ///
99    /// Create a style with bold text:
100    ///
101    /// ```
102    /// use std::io::Write;
103    ///
104    /// let mut builder = env_logger::Builder::new();
105    ///
106    /// builder.format(|buf, record| {
107    ///     let mut style = buf.style();
108    ///
109    ///     style.set_bold(true);
110    ///
111    ///     writeln!(buf, "{}", style.value(record.args()))
112    /// });
113    /// ```
114    pub fn set_bold(&mut self, yes: bool) -> &mut Style {
115        self.spec.set_bold(yes);
116        self
117    }
118
119    /// Set the text intensity.
120    ///
121    /// If `yes` is true then text will be written in a brighter color.
122    /// If `yes` is false then text will be written in the default color.
123    ///
124    /// # Examples
125    ///
126    /// Create a style with intense text:
127    ///
128    /// ```
129    /// use std::io::Write;
130    ///
131    /// let mut builder = env_logger::Builder::new();
132    ///
133    /// builder.format(|buf, record| {
134    ///     let mut style = buf.style();
135    ///
136    ///     style.set_intense(true);
137    ///
138    ///     writeln!(buf, "{}", style.value(record.args()))
139    /// });
140    /// ```
141    pub fn set_intense(&mut self, yes: bool) -> &mut Style {
142        self.spec.set_intense(yes);
143        self
144    }
145
146    /// Set whether the text is dimmed.
147    ///
148    /// If `yes` is true then text will be written in a dimmer color.
149    /// If `yes` is false then text will be written in the default color.
150    ///
151    /// # Examples
152    ///
153    /// Create a style with dimmed text:
154    ///
155    /// ```
156    /// use std::io::Write;
157    ///
158    /// let mut builder = env_logger::Builder::new();
159    ///
160    /// builder.format(|buf, record| {
161    ///     let mut style = buf.style();
162    ///
163    ///     style.set_dimmed(true);
164    ///
165    ///     writeln!(buf, "{}", style.value(record.args()))
166    /// });
167    /// ```
168    pub fn set_dimmed(&mut self, yes: bool) -> &mut Style {
169        self.spec.set_dimmed(yes);
170        self
171    }
172
173    /// Set the background color.
174    ///
175    /// # Examples
176    ///
177    /// Create a style with a yellow background:
178    ///
179    /// ```
180    /// use std::io::Write;
181    /// use env_logger::fmt::Color;
182    ///
183    /// let mut builder = env_logger::Builder::new();
184    ///
185    /// builder.format(|buf, record| {
186    ///     let mut style = buf.style();
187    ///
188    ///     style.set_bg(Color::Yellow);
189    ///
190    ///     writeln!(buf, "{}", style.value(record.args()))
191    /// });
192    /// ```
193    pub fn set_bg(&mut self, color: Color) -> &mut Style {
194        self.spec.set_bg(Some(color.into_termcolor()));
195        self
196    }
197
198    /// Wrap a value in the style.
199    ///
200    /// The same `Style` can be used to print multiple different values.
201    ///
202    /// # Examples
203    ///
204    /// Create a bold, red colored style and use it to print the log level:
205    ///
206    /// ```
207    /// use std::io::Write;
208    /// use env_logger::fmt::Color;
209    ///
210    /// let mut builder = env_logger::Builder::new();
211    ///
212    /// builder.format(|buf, record| {
213    ///     let mut style = buf.style();
214    ///
215    ///     style.set_color(Color::Red).set_bold(true);
216    ///
217    ///     writeln!(buf, "{}: {}",
218    ///         style.value(record.level()),
219    ///         record.args())
220    /// });
221    /// ```
222    pub fn value<T>(&self, value: T) -> StyledValue<T> {
223        StyledValue {
224            style: Cow::Borrowed(self),
225            value,
226        }
227    }
228
229    /// Wrap a value in the style by taking ownership of it.
230    pub(crate) fn into_value<T>(self, value: T) -> StyledValue<'static, T> {
231        StyledValue {
232            style: Cow::Owned(self),
233            value,
234        }
235    }
236}
237
238impl fmt::Debug for Style {
239    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
240        f.debug_struct("Style").field("spec", &self.spec).finish()
241    }
242}
243
244/// A value that can be printed using the given styles.
245///
246/// It is the result of calling [`Style::value`].
247///
248/// [`Style::value`]: struct.Style.html#method.value
249pub struct StyledValue<'a, T> {
250    style: Cow<'a, Style>,
251    value: T,
252}
253
254impl<'a, T> StyledValue<'a, T> {
255    fn write_fmt<F>(&self, f: F) -> fmt::Result
256    where
257        F: FnOnce() -> fmt::Result,
258    {
259        self.style
260            .buf
261            .borrow_mut()
262            .set_color(&self.style.spec)
263            .map_err(|_| fmt::Error)?;
264
265        // Always try to reset the terminal style, even if writing failed
266        let write = f();
267        let reset = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error);
268
269        write.and(reset)
270    }
271}
272
273macro_rules! impl_styled_value_fmt {
274    ($($fmt_trait:path),*) => {
275        $(
276            impl<'a, T: $fmt_trait> $fmt_trait for StyledValue<'a, T> {
277                fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result {
278                    self.write_fmt(|| T::fmt(&self.value, f))
279                }
280            }
281        )*
282    };
283}
284
285impl_styled_value_fmt!(
286    fmt::Debug,
287    fmt::Display,
288    fmt::Pointer,
289    fmt::Octal,
290    fmt::Binary,
291    fmt::UpperHex,
292    fmt::LowerHex,
293    fmt::UpperExp,
294    fmt::LowerExp
295);
296
297// The `Color` type is copied from https://github.com/BurntSushi/termcolor
298
299/// The set of available colors for the terminal foreground/background.
300///
301/// The `Ansi256` and `Rgb` colors will only output the correct codes when
302/// paired with the `Ansi` `WriteColor` implementation.
303///
304/// The `Ansi256` and `Rgb` color types are not supported when writing colors
305/// on Windows using the console. If they are used on Windows, then they are
306/// silently ignored and no colors will be emitted.
307///
308/// This set may expand over time.
309///
310/// This type has a `FromStr` impl that can parse colors from their human
311/// readable form. The format is as follows:
312///
313/// 1. Any of the explicitly listed colors in English. They are matched
314///    case insensitively.
315/// 2. A single 8-bit integer, in either decimal or hexadecimal format.
316/// 3. A triple of 8-bit integers separated by a comma, where each integer is
317///    in decimal or hexadecimal format.
318///
319/// Hexadecimal numbers are written with a `0x` prefix.
320#[allow(missing_docs)]
321#[non_exhaustive]
322#[derive(Clone, Debug, Eq, PartialEq)]
323pub enum Color {
324    Black,
325    Blue,
326    Green,
327    Red,
328    Cyan,
329    Magenta,
330    Yellow,
331    White,
332    Ansi256(u8),
333    Rgb(u8, u8, u8),
334}
335
336impl Color {
337    fn into_termcolor(self) -> termcolor::Color {
338        match self {
339            Color::Black => termcolor::Color::Black,
340            Color::Blue => termcolor::Color::Blue,
341            Color::Green => termcolor::Color::Green,
342            Color::Red => termcolor::Color::Red,
343            Color::Cyan => termcolor::Color::Cyan,
344            Color::Magenta => termcolor::Color::Magenta,
345            Color::Yellow => termcolor::Color::Yellow,
346            Color::White => termcolor::Color::White,
347            Color::Ansi256(value) => termcolor::Color::Ansi256(value),
348            Color::Rgb(r, g, b) => termcolor::Color::Rgb(r, g, b),
349        }
350    }
351}