env_logger/fmt/writer/termcolor/
extern_impl.rs

1use std::borrow::Cow;
2use std::cell::RefCell;
3use std::fmt;
4use std::io::{self, Write};
5use std::rc::Rc;
6
7use log::Level;
8use termcolor::{self, ColorChoice, ColorSpec, WriteColor};
9
10use crate::fmt::{Formatter, Target, WriteStyle};
11
12pub(in crate::fmt::writer) mod glob {
13    pub use super::*;
14}
15
16impl Formatter {
17    /// Begin a new [`Style`].
18    ///
19    /// # Examples
20    ///
21    /// Create a bold, red colored style and use it to print the log level:
22    ///
23    /// ```
24    /// use std::io::Write;
25    /// use env_logger::fmt::Color;
26    ///
27    /// let mut builder = env_logger::Builder::new();
28    ///
29    /// builder.format(|buf, record| {
30    ///     let mut level_style = buf.style();
31    ///
32    ///     level_style.set_color(Color::Red).set_bold(true);
33    ///
34    ///     writeln!(buf, "{}: {}",
35    ///         level_style.value(record.level()),
36    ///         record.args())
37    /// });
38    /// ```
39    ///
40    /// [`Style`]: struct.Style.html
41    pub fn style(&self) -> Style {
42        Style {
43            buf: self.buf.clone(),
44            spec: ColorSpec::new(),
45        }
46    }
47
48    /// Get the default [`Style`] for the given level.
49    ///
50    /// The style can be used to print other values besides the level.
51    pub fn default_level_style(&self, level: Level) -> Style {
52        let mut level_style = self.style();
53        match level {
54            Level::Trace => level_style.set_color(Color::Black).set_intense(true),
55            Level::Debug => level_style.set_color(Color::White),
56            Level::Info => level_style.set_color(Color::Green),
57            Level::Warn => level_style.set_color(Color::Yellow),
58            Level::Error => level_style.set_color(Color::Red).set_bold(true),
59        };
60        level_style
61    }
62
63    /// Get a printable [`Style`] for the given level.
64    ///
65    /// The style can only be used to print the level.
66    pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> {
67        self.default_level_style(level).into_value(level)
68    }
69}
70
71pub(in crate::fmt::writer) struct BufferWriter {
72    inner: termcolor::BufferWriter,
73    test_target: Option<Target>,
74}
75
76pub(in crate::fmt) struct Buffer {
77    inner: termcolor::Buffer,
78    test_target: Option<Target>,
79}
80
81impl BufferWriter {
82    pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self {
83        BufferWriter {
84            inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
85            test_target: if is_test { Some(Target::Stderr) } else { None },
86        }
87    }
88
89    pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self {
90        BufferWriter {
91            inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()),
92            test_target: if is_test { Some(Target::Stdout) } else { None },
93        }
94    }
95
96    pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
97        Buffer {
98            inner: self.inner.buffer(),
99            test_target: self.test_target,
100        }
101    }
102
103    pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
104        if let Some(target) = self.test_target {
105            // This impl uses the `eprint` and `print` macros
106            // instead of `termcolor`'s buffer.
107            // This is so their output can be captured by `cargo test`
108            let log = String::from_utf8_lossy(buf.bytes());
109
110            match target {
111                Target::Stderr => eprint!("{}", log),
112                Target::Stdout => print!("{}", log),
113            }
114
115            Ok(())
116        } else {
117            self.inner.print(&buf.inner)
118        }
119    }
120}
121
122impl Buffer {
123    pub(in crate::fmt) fn clear(&mut self) {
124        self.inner.clear()
125    }
126
127    pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
128        self.inner.write(buf)
129    }
130
131    pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
132        self.inner.flush()
133    }
134
135    pub(in crate::fmt) fn bytes(&self) -> &[u8] {
136        self.inner.as_slice()
137    }
138
139    fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
140        // Ignore styles for test captured logs because they can't be printed
141        if self.test_target.is_none() {
142            self.inner.set_color(spec)
143        } else {
144            Ok(())
145        }
146    }
147
148    fn reset(&mut self) -> io::Result<()> {
149        // Ignore styles for test captured logs because they can't be printed
150        if self.test_target.is_none() {
151            self.inner.reset()
152        } else {
153            Ok(())
154        }
155    }
156}
157
158impl WriteStyle {
159    fn into_color_choice(self) -> ColorChoice {
160        match self {
161            WriteStyle::Always => ColorChoice::Always,
162            WriteStyle::Auto => ColorChoice::Auto,
163            WriteStyle::Never => ColorChoice::Never,
164        }
165    }
166}
167
168/// A set of styles to apply to the terminal output.
169///
170/// Call [`Formatter::style`] to get a `Style` and use the builder methods to
171/// set styling properties, like [color] and [weight].
172/// To print a value using the style, wrap it in a call to [`value`] when the log
173/// record is formatted.
174///
175/// # Examples
176///
177/// Create a bold, red colored style and use it to print the log level:
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 level_style = buf.style();
187///
188///     level_style.set_color(Color::Red).set_bold(true);
189///
190///     writeln!(buf, "{}: {}",
191///         level_style.value(record.level()),
192///         record.args())
193/// });
194/// ```
195///
196/// Styles can be re-used to output multiple values:
197///
198/// ```
199/// use std::io::Write;
200/// use env_logger::fmt::Color;
201///
202/// let mut builder = env_logger::Builder::new();
203///
204/// builder.format(|buf, record| {
205///     let mut bold = buf.style();
206///
207///     bold.set_bold(true);
208///
209///     writeln!(buf, "{}: {} {}",
210///         bold.value(record.level()),
211///         bold.value("some bold text"),
212///         record.args())
213/// });
214/// ```
215///
216/// [`Formatter::style`]: struct.Formatter.html#method.style
217/// [color]: #method.set_color
218/// [weight]: #method.set_bold
219/// [`value`]: #method.value
220#[derive(Clone)]
221pub struct Style {
222    buf: Rc<RefCell<Buffer>>,
223    spec: ColorSpec,
224}
225
226/// A value that can be printed using the given styles.
227///
228/// It is the result of calling [`Style::value`].
229///
230/// [`Style::value`]: struct.Style.html#method.value
231pub struct StyledValue<'a, T> {
232    style: Cow<'a, Style>,
233    value: T,
234}
235
236impl Style {
237    /// Set the text color.
238    ///
239    /// # Examples
240    ///
241    /// Create a style with red text:
242    ///
243    /// ```
244    /// use std::io::Write;
245    /// use env_logger::fmt::Color;
246    ///
247    /// let mut builder = env_logger::Builder::new();
248    ///
249    /// builder.format(|buf, record| {
250    ///     let mut style = buf.style();
251    ///
252    ///     style.set_color(Color::Red);
253    ///
254    ///     writeln!(buf, "{}", style.value(record.args()))
255    /// });
256    /// ```
257    pub fn set_color(&mut self, color: Color) -> &mut Style {
258        self.spec.set_fg(color.into_termcolor());
259        self
260    }
261
262    /// Set the text weight.
263    ///
264    /// If `yes` is true then text will be written in bold.
265    /// If `yes` is false then text will be written in the default weight.
266    ///
267    /// # Examples
268    ///
269    /// Create a style with bold text:
270    ///
271    /// ```
272    /// use std::io::Write;
273    ///
274    /// let mut builder = env_logger::Builder::new();
275    ///
276    /// builder.format(|buf, record| {
277    ///     let mut style = buf.style();
278    ///
279    ///     style.set_bold(true);
280    ///
281    ///     writeln!(buf, "{}", style.value(record.args()))
282    /// });
283    /// ```
284    pub fn set_bold(&mut self, yes: bool) -> &mut Style {
285        self.spec.set_bold(yes);
286        self
287    }
288
289    /// Set the text intensity.
290    ///
291    /// If `yes` is true then text will be written in a brighter color.
292    /// If `yes` is false then text will be written in the default color.
293    ///
294    /// # Examples
295    ///
296    /// Create a style with intense text:
297    ///
298    /// ```
299    /// use std::io::Write;
300    ///
301    /// let mut builder = env_logger::Builder::new();
302    ///
303    /// builder.format(|buf, record| {
304    ///     let mut style = buf.style();
305    ///
306    ///     style.set_intense(true);
307    ///
308    ///     writeln!(buf, "{}", style.value(record.args()))
309    /// });
310    /// ```
311    pub fn set_intense(&mut self, yes: bool) -> &mut Style {
312        self.spec.set_intense(yes);
313        self
314    }
315
316    /// Set the background color.
317    ///
318    /// # Examples
319    ///
320    /// Create a style with a yellow background:
321    ///
322    /// ```
323    /// use std::io::Write;
324    /// use env_logger::fmt::Color;
325    ///
326    /// let mut builder = env_logger::Builder::new();
327    ///
328    /// builder.format(|buf, record| {
329    ///     let mut style = buf.style();
330    ///
331    ///     style.set_bg(Color::Yellow);
332    ///
333    ///     writeln!(buf, "{}", style.value(record.args()))
334    /// });
335    /// ```
336    pub fn set_bg(&mut self, color: Color) -> &mut Style {
337        self.spec.set_bg(color.into_termcolor());
338        self
339    }
340
341    /// Wrap a value in the style.
342    ///
343    /// The same `Style` can be used to print multiple different values.
344    ///
345    /// # Examples
346    ///
347    /// Create a bold, red colored style and use it to print the log level:
348    ///
349    /// ```
350    /// use std::io::Write;
351    /// use env_logger::fmt::Color;
352    ///
353    /// let mut builder = env_logger::Builder::new();
354    ///
355    /// builder.format(|buf, record| {
356    ///     let mut style = buf.style();
357    ///
358    ///     style.set_color(Color::Red).set_bold(true);
359    ///
360    ///     writeln!(buf, "{}: {}",
361    ///         style.value(record.level()),
362    ///         record.args())
363    /// });
364    /// ```
365    pub fn value<T>(&self, value: T) -> StyledValue<T> {
366        StyledValue {
367            style: Cow::Borrowed(self),
368            value,
369        }
370    }
371
372    /// Wrap a value in the style by taking ownership of it.
373    pub(crate) fn into_value<T>(&mut self, value: T) -> StyledValue<'static, T> {
374        StyledValue {
375            style: Cow::Owned(self.clone()),
376            value,
377        }
378    }
379}
380
381impl<'a, T> StyledValue<'a, T> {
382    fn write_fmt<F>(&self, f: F) -> fmt::Result
383    where
384        F: FnOnce() -> fmt::Result,
385    {
386        self.style
387            .buf
388            .borrow_mut()
389            .set_color(&self.style.spec)
390            .map_err(|_| fmt::Error)?;
391
392        // Always try to reset the terminal style, even if writing failed
393        let write = f();
394        let reset = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error);
395
396        write.and(reset)
397    }
398}
399
400impl fmt::Debug for Style {
401    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
402        f.debug_struct("Style").field("spec", &self.spec).finish()
403    }
404}
405
406macro_rules! impl_styled_value_fmt {
407    ($($fmt_trait:path),*) => {
408        $(
409            impl<'a, T: $fmt_trait> $fmt_trait for StyledValue<'a, T> {
410                fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result {
411                    self.write_fmt(|| T::fmt(&self.value, f))
412                }
413            }
414        )*
415    };
416}
417
418impl_styled_value_fmt!(
419    fmt::Debug,
420    fmt::Display,
421    fmt::Pointer,
422    fmt::Octal,
423    fmt::Binary,
424    fmt::UpperHex,
425    fmt::LowerHex,
426    fmt::UpperExp,
427    fmt::LowerExp
428);
429
430// The `Color` type is copied from https://github.com/BurntSushi/ripgrep/tree/master/termcolor
431
432/// The set of available colors for the terminal foreground/background.
433///
434/// The `Ansi256` and `Rgb` colors will only output the correct codes when
435/// paired with the `Ansi` `WriteColor` implementation.
436///
437/// The `Ansi256` and `Rgb` color types are not supported when writing colors
438/// on Windows using the console. If they are used on Windows, then they are
439/// silently ignored and no colors will be emitted.
440///
441/// This set may expand over time.
442///
443/// This type has a `FromStr` impl that can parse colors from their human
444/// readable form. The format is as follows:
445///
446/// 1. Any of the explicitly listed colors in English. They are matched
447///    case insensitively.
448/// 2. A single 8-bit integer, in either decimal or hexadecimal format.
449/// 3. A triple of 8-bit integers separated by a comma, where each integer is
450///    in decimal or hexadecimal format.
451///
452/// Hexadecimal numbers are written with a `0x` prefix.
453#[allow(missing_docs)]
454#[derive(Clone, Debug, Eq, PartialEq)]
455pub enum Color {
456    Black,
457    Blue,
458    Green,
459    Red,
460    Cyan,
461    Magenta,
462    Yellow,
463    White,
464    Ansi256(u8),
465    Rgb(u8, u8, u8),
466    #[doc(hidden)]
467    __Nonexhaustive,
468}
469
470impl Color {
471    fn into_termcolor(self) -> Option<termcolor::Color> {
472        match self {
473            Color::Black => Some(termcolor::Color::Black),
474            Color::Blue => Some(termcolor::Color::Blue),
475            Color::Green => Some(termcolor::Color::Green),
476            Color::Red => Some(termcolor::Color::Red),
477            Color::Cyan => Some(termcolor::Color::Cyan),
478            Color::Magenta => Some(termcolor::Color::Magenta),
479            Color::Yellow => Some(termcolor::Color::Yellow),
480            Color::White => Some(termcolor::Color::White),
481            Color::Ansi256(value) => Some(termcolor::Color::Ansi256(value)),
482            Color::Rgb(r, g, b) => Some(termcolor::Color::Rgb(r, g, b)),
483            _ => None,
484        }
485    }
486}