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