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