summaryrefslogtreecommitdiffstats
path: root/vendor/env_logger/src/fmt/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/env_logger/src/fmt/mod.rs')
-rw-r--r--vendor/env_logger/src/fmt/mod.rs652
1 files changed, 652 insertions, 0 deletions
diff --git a/vendor/env_logger/src/fmt/mod.rs b/vendor/env_logger/src/fmt/mod.rs
new file mode 100644
index 000000000..21e09577a
--- /dev/null
+++ b/vendor/env_logger/src/fmt/mod.rs
@@ -0,0 +1,652 @@
+//! Formatting for log records.
+//!
+//! This module contains a [`Formatter`] that can be used to format log records
+//! into without needing temporary allocations. Usually you won't need to worry
+//! about the contents of this module and can use the `Formatter` like an ordinary
+//! [`Write`].
+//!
+//! # Formatting log records
+//!
+//! The format used to print log records can be customised using the [`Builder::format`]
+//! method.
+//! Custom formats can apply different color and weight to printed values using
+//! [`Style`] builders.
+//!
+//! ```
+//! use std::io::Write;
+//!
+//! let mut builder = env_logger::Builder::new();
+//!
+//! builder.format(|buf, record| {
+//! writeln!(buf, "{}: {}",
+//! record.level(),
+//! record.args())
+//! });
+//! ```
+//!
+//! [`Formatter`]: struct.Formatter.html
+//! [`Style`]: struct.Style.html
+//! [`Builder::format`]: ../struct.Builder.html#method.format
+//! [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
+
+use std::cell::RefCell;
+use std::fmt::Display;
+use std::io::prelude::*;
+use std::rc::Rc;
+use std::{fmt, io, mem};
+
+use log::Record;
+
+mod humantime;
+pub(crate) mod writer;
+
+pub use self::humantime::glob::*;
+pub use self::writer::glob::*;
+
+use self::writer::{Buffer, Writer};
+
+pub(crate) mod glob {
+ pub use super::{Target, TimestampPrecision, WriteStyle};
+}
+
+/// Formatting precision of timestamps.
+///
+/// Seconds give precision of full seconds, milliseconds give thousands of a
+/// second (3 decimal digits), microseconds are millionth of a second (6 decimal
+/// digits) and nanoseconds are billionth of a second (9 decimal digits).
+#[derive(Copy, Clone, Debug)]
+pub enum TimestampPrecision {
+ /// Full second precision (0 decimal digits)
+ Seconds,
+ /// Millisecond precision (3 decimal digits)
+ Millis,
+ /// Microsecond precision (6 decimal digits)
+ Micros,
+ /// Nanosecond precision (9 decimal digits)
+ Nanos,
+}
+
+/// The default timestamp precision is seconds.
+impl Default for TimestampPrecision {
+ fn default() -> Self {
+ TimestampPrecision::Seconds
+ }
+}
+
+/// A formatter to write logs into.
+///
+/// `Formatter` implements the standard [`Write`] trait for writing log records.
+/// It also supports terminal colors, through the [`style`] method.
+///
+/// # Examples
+///
+/// Use the [`writeln`] macro to format a log record.
+/// An instance of a `Formatter` is passed to an `env_logger` format as `buf`:
+///
+/// ```
+/// use std::io::Write;
+///
+/// let mut builder = env_logger::Builder::new();
+///
+/// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()));
+/// ```
+///
+/// [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
+/// [`writeln`]: https://doc.rust-lang.org/stable/std/macro.writeln.html
+/// [`style`]: #method.style
+pub struct Formatter {
+ buf: Rc<RefCell<Buffer>>,
+ write_style: WriteStyle,
+}
+
+impl Formatter {
+ pub(crate) fn new(writer: &Writer) -> Self {
+ Formatter {
+ buf: Rc::new(RefCell::new(writer.buffer())),
+ write_style: writer.write_style(),
+ }
+ }
+
+ pub(crate) fn write_style(&self) -> WriteStyle {
+ self.write_style
+ }
+
+ pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
+ writer.print(&self.buf.borrow())
+ }
+
+ pub(crate) fn clear(&mut self) {
+ self.buf.borrow_mut().clear()
+ }
+}
+
+impl Write for Formatter {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.buf.borrow_mut().write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.buf.borrow_mut().flush()
+ }
+}
+
+impl fmt::Debug for Formatter {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Formatter").finish()
+ }
+}
+
+pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>;
+
+pub(crate) struct Builder {
+ pub format_timestamp: Option<TimestampPrecision>,
+ pub format_module_path: bool,
+ pub format_target: bool,
+ pub format_level: bool,
+ pub format_indent: Option<usize>,
+ pub custom_format: Option<FormatFn>,
+ pub format_suffix: &'static str,
+ built: bool,
+}
+
+impl Default for Builder {
+ fn default() -> Self {
+ Builder {
+ format_timestamp: Some(Default::default()),
+ format_module_path: false,
+ format_target: true,
+ format_level: true,
+ format_indent: Some(4),
+ custom_format: None,
+ format_suffix: "\n",
+ built: false,
+ }
+ }
+}
+
+impl Builder {
+ /// Convert the format into a callable function.
+ ///
+ /// If the `custom_format` is `Some`, then any `default_format` switches are ignored.
+ /// If the `custom_format` is `None`, then a default format is returned.
+ /// Any `default_format` switches set to `false` won't be written by the format.
+ pub fn build(&mut self) -> FormatFn {
+ assert!(!self.built, "attempt to re-use consumed builder");
+
+ let built = mem::replace(
+ self,
+ Builder {
+ built: true,
+ ..Default::default()
+ },
+ );
+
+ if let Some(fmt) = built.custom_format {
+ fmt
+ } else {
+ Box::new(move |buf, record| {
+ let fmt = DefaultFormat {
+ timestamp: built.format_timestamp,
+ module_path: built.format_module_path,
+ target: built.format_target,
+ level: built.format_level,
+ written_header_value: false,
+ indent: built.format_indent,
+ suffix: built.format_suffix,
+ buf,
+ };
+
+ fmt.write(record)
+ })
+ }
+ }
+}
+
+#[cfg(feature = "termcolor")]
+type SubtleStyle = StyledValue<'static, &'static str>;
+#[cfg(not(feature = "termcolor"))]
+type SubtleStyle = &'static str;
+
+/// The default format.
+///
+/// This format needs to work with any combination of crate features.
+struct DefaultFormat<'a> {
+ timestamp: Option<TimestampPrecision>,
+ module_path: bool,
+ target: bool,
+ level: bool,
+ written_header_value: bool,
+ indent: Option<usize>,
+ buf: &'a mut Formatter,
+ suffix: &'a str,
+}
+
+impl<'a> DefaultFormat<'a> {
+ fn write(mut self, record: &Record) -> io::Result<()> {
+ self.write_timestamp()?;
+ self.write_level(record)?;
+ self.write_module_path(record)?;
+ self.write_target(record)?;
+ self.finish_header()?;
+
+ self.write_args(record)
+ }
+
+ fn subtle_style(&self, text: &'static str) -> SubtleStyle {
+ #[cfg(feature = "termcolor")]
+ {
+ self.buf
+ .style()
+ .set_color(Color::Black)
+ .set_intense(true)
+ .clone()
+ .into_value(text)
+ }
+ #[cfg(not(feature = "termcolor"))]
+ {
+ text
+ }
+ }
+
+ fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
+ where
+ T: Display,
+ {
+ if !self.written_header_value {
+ self.written_header_value = true;
+
+ let open_brace = self.subtle_style("[");
+ write!(self.buf, "{}{}", open_brace, value)
+ } else {
+ write!(self.buf, " {}", value)
+ }
+ }
+
+ fn write_level(&mut self, record: &Record) -> io::Result<()> {
+ if !self.level {
+ return Ok(());
+ }
+
+ let level = {
+ #[cfg(feature = "termcolor")]
+ {
+ self.buf.default_styled_level(record.level())
+ }
+ #[cfg(not(feature = "termcolor"))]
+ {
+ record.level()
+ }
+ };
+
+ self.write_header_value(format_args!("{:<5}", level))
+ }
+
+ fn write_timestamp(&mut self) -> io::Result<()> {
+ #[cfg(feature = "humantime")]
+ {
+ use self::TimestampPrecision::*;
+ let ts = match self.timestamp {
+ None => return Ok(()),
+ Some(Seconds) => self.buf.timestamp_seconds(),
+ Some(Millis) => self.buf.timestamp_millis(),
+ Some(Micros) => self.buf.timestamp_micros(),
+ Some(Nanos) => self.buf.timestamp_nanos(),
+ };
+
+ self.write_header_value(ts)
+ }
+ #[cfg(not(feature = "humantime"))]
+ {
+ // Trick the compiler to think we have used self.timestamp
+ // Workaround for "field is never used: `timestamp`" compiler nag.
+ let _ = self.timestamp;
+ Ok(())
+ }
+ }
+
+ fn write_module_path(&mut self, record: &Record) -> io::Result<()> {
+ if !self.module_path {
+ return Ok(());
+ }
+
+ if let Some(module_path) = record.module_path() {
+ self.write_header_value(module_path)
+ } else {
+ Ok(())
+ }
+ }
+
+ fn write_target(&mut self, record: &Record) -> io::Result<()> {
+ if !self.target {
+ return Ok(());
+ }
+
+ match record.target() {
+ "" => Ok(()),
+ target => self.write_header_value(target),
+ }
+ }
+
+ fn finish_header(&mut self) -> io::Result<()> {
+ if self.written_header_value {
+ let close_brace = self.subtle_style("]");
+ write!(self.buf, "{} ", close_brace)
+ } else {
+ Ok(())
+ }
+ }
+
+ fn write_args(&mut self, record: &Record) -> io::Result<()> {
+ match self.indent {
+ // Fast path for no indentation
+ None => write!(self.buf, "{}{}", record.args(), self.suffix),
+
+ Some(indent_count) => {
+ // Create a wrapper around the buffer only if we have to actually indent the message
+
+ struct IndentWrapper<'a, 'b: 'a> {
+ fmt: &'a mut DefaultFormat<'b>,
+ indent_count: usize,
+ }
+
+ impl<'a, 'b> Write for IndentWrapper<'a, 'b> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let mut first = true;
+ for chunk in buf.split(|&x| x == b'\n') {
+ if !first {
+ write!(
+ self.fmt.buf,
+ "{}{:width$}",
+ self.fmt.suffix,
+ "",
+ width = self.indent_count
+ )?;
+ }
+ self.fmt.buf.write_all(chunk)?;
+ first = false;
+ }
+
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.fmt.buf.flush()
+ }
+ }
+
+ // The explicit scope here is just to make older versions of Rust happy
+ {
+ let mut wrapper = IndentWrapper {
+ fmt: self,
+ indent_count,
+ };
+ write!(wrapper, "{}", record.args())?;
+ }
+
+ write!(self.buf, "{}", self.suffix)?;
+
+ Ok(())
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use log::{Level, Record};
+
+ fn write_record(record: Record, fmt: DefaultFormat) -> String {
+ let buf = fmt.buf.buf.clone();
+
+ fmt.write(&record).expect("failed to write record");
+
+ let buf = buf.borrow();
+ String::from_utf8(buf.bytes().to_vec()).expect("failed to read record")
+ }
+
+ fn write_target<'a>(target: &'a str, fmt: DefaultFormat) -> String {
+ write_record(
+ Record::builder()
+ .args(format_args!("log\nmessage"))
+ .level(Level::Info)
+ .file(Some("test.rs"))
+ .line(Some(144))
+ .module_path(Some("test::path"))
+ .target(target)
+ .build(),
+ fmt,
+ )
+ }
+
+ fn write(fmt: DefaultFormat) -> String {
+ write_target("", fmt)
+ }
+
+ #[test]
+ fn format_with_header() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: false,
+ level: true,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("[INFO test::path] log\nmessage\n", written);
+ }
+
+ #[test]
+ fn format_no_header() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: false,
+ target: false,
+ level: false,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("log\nmessage\n", written);
+ }
+
+ #[test]
+ fn format_indent_spaces() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: false,
+ level: true,
+ written_header_value: false,
+ indent: Some(4),
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("[INFO test::path] log\n message\n", written);
+ }
+
+ #[test]
+ fn format_indent_zero_spaces() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: false,
+ level: true,
+ written_header_value: false,
+ indent: Some(0),
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("[INFO test::path] log\nmessage\n", written);
+ }
+
+ #[test]
+ fn format_indent_spaces_no_header() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: false,
+ target: false,
+ level: false,
+ written_header_value: false,
+ indent: Some(4),
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("log\n message\n", written);
+ }
+
+ #[test]
+ fn format_suffix() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: false,
+ target: false,
+ level: false,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("log\nmessage\n\n", written);
+ }
+
+ #[test]
+ fn format_suffix_with_indent() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: false,
+ target: false,
+ level: false,
+ written_header_value: false,
+ indent: Some(4),
+ suffix: "\n\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("log\n\n message\n\n", written);
+ }
+
+ #[test]
+ fn format_target() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write_target(
+ "target",
+ DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: true,
+ level: true,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n",
+ buf: &mut f,
+ },
+ );
+
+ assert_eq!("[INFO test::path target] log\nmessage\n", written);
+ }
+
+ #[test]
+ fn format_empty_target() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write(DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: true,
+ level: true,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n",
+ buf: &mut f,
+ });
+
+ assert_eq!("[INFO test::path] log\nmessage\n", written);
+ }
+
+ #[test]
+ fn format_no_target() {
+ let writer = writer::Builder::new()
+ .write_style(WriteStyle::Never)
+ .build();
+
+ let mut f = Formatter::new(&writer);
+
+ let written = write_target(
+ "target",
+ DefaultFormat {
+ timestamp: None,
+ module_path: true,
+ target: false,
+ level: true,
+ written_header_value: false,
+ indent: None,
+ suffix: "\n",
+ buf: &mut f,
+ },
+ );
+
+ assert_eq!("[INFO test::path] log\nmessage\n", written);
+ }
+}