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}