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 formatting
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: Result<(), Error> = f();
442 let reset: Result<(), Error> = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error);
443
444 write.and(res: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(name:"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: u8) => termcolor::Color::Ansi256(value),
529 Color::Rgb(r: u8, g: u8, b: u8) => termcolor::Color::Rgb(r, g, b),
530 }
531 }
532}
533