• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     uncolored_target: Option<WritableTarget>,
75 }
76 
77 pub(in crate::fmt) struct Buffer {
78     inner: termcolor::Buffer,
79     has_uncolored_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             uncolored_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             uncolored_target: if is_test {
98                 Some(WritableTarget::Stdout)
99             } else {
100                 None
101             },
102         }
103     }
104 
pipe( write_style: WriteStyle, pipe: Box<Mutex<dyn io::Write + Send + 'static>>, ) -> Self105     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 formatting
111             inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
112             uncolored_target: Some(WritableTarget::Pipe(pipe)),
113         }
114     }
115 
buffer(&self) -> Buffer116     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 
print(&self, buf: &Buffer) -> io::Result<()>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 
143 impl Buffer {
clear(&mut self)144     pub(in crate::fmt) fn clear(&mut self) {
145         self.inner.clear()
146     }
147 
write(&mut self, buf: &[u8]) -> io::Result<usize>148     pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
149         self.inner.write(buf)
150     }
151 
flush(&mut self) -> io::Result<()>152     pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
153         self.inner.flush()
154     }
155 
bytes(&self) -> &[u8]156     pub(in crate::fmt) fn bytes(&self) -> &[u8] {
157         self.inner.as_slice()
158     }
159 
set_color(&mut self, spec: &ColorSpec) -> io::Result<()>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 
reset(&mut self) -> io::Result<()>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 
179 impl WriteStyle {
into_color_choice(self) -> ColorChoice180     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)]
242 pub 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
252 pub struct StyledValue<'a, T> {
253     style: Cow<'a, Style>,
254     value: T,
255 }
256 
257 impl 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     /// ```
set_color(&mut self, color: Color) -> &mut Style278     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     /// ```
set_bold(&mut self, yes: bool) -> &mut Style305     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     /// ```
set_intense(&mut self, yes: bool) -> &mut Style332     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     /// ```
set_dimmed(&mut self, yes: bool) -> &mut Style359     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     /// ```
set_bg(&mut self, color: Color) -> &mut Style384     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     /// ```
value<T>(&self, value: T) -> StyledValue<T>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.
into_value<T>(self, value: T) -> StyledValue<'static, T>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 
429 impl<'a, T> StyledValue<'a, T> {
write_fmt<F>(&self, f: F) -> fmt::Result where F: FnOnce() -> fmt::Result,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 
448 impl fmt::Debug for Style {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result449     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
450         f.debug_struct("Style").field("spec", &self.spec).finish()
451     }
452 }
453 
454 macro_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 
466 impl_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)]
504 pub 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 
517 impl Color {
into_termcolor(self) -> termcolor::Color518     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 }
533