From 64d98f8ee037282c35007b64c2649055c56af1db Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:03 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- vendor/env_logger/src/fmt/writer/atty.rs | 33 ++ vendor/env_logger/src/fmt/writer/mod.rs | 253 ++++++++++ .../src/fmt/writer/termcolor/extern_impl.rs | 532 +++++++++++++++++++++ vendor/env_logger/src/fmt/writer/termcolor/mod.rs | 12 + .../src/fmt/writer/termcolor/shim_impl.rs | 72 +++ 5 files changed, 902 insertions(+) create mode 100644 vendor/env_logger/src/fmt/writer/atty.rs create mode 100644 vendor/env_logger/src/fmt/writer/mod.rs create mode 100644 vendor/env_logger/src/fmt/writer/termcolor/extern_impl.rs create mode 100644 vendor/env_logger/src/fmt/writer/termcolor/mod.rs create mode 100644 vendor/env_logger/src/fmt/writer/termcolor/shim_impl.rs (limited to 'vendor/env_logger/src/fmt/writer') diff --git a/vendor/env_logger/src/fmt/writer/atty.rs b/vendor/env_logger/src/fmt/writer/atty.rs new file mode 100644 index 000000000..1a133eeff --- /dev/null +++ b/vendor/env_logger/src/fmt/writer/atty.rs @@ -0,0 +1,33 @@ +/* +This internal module contains the terminal detection implementation. + +If the `auto-color` feature is enabled then we detect whether we're attached to a particular TTY. +Otherwise, assume we're not attached to anything. This effectively prevents styles from being +printed. +*/ + +#[cfg(feature = "auto-color")] +mod imp { + use is_terminal::IsTerminal; + + pub(in crate::fmt) fn is_stdout() -> bool { + std::io::stdout().is_terminal() + } + + pub(in crate::fmt) fn is_stderr() -> bool { + std::io::stderr().is_terminal() + } +} + +#[cfg(not(feature = "auto-color"))] +mod imp { + pub(in crate::fmt) fn is_stdout() -> bool { + false + } + + pub(in crate::fmt) fn is_stderr() -> bool { + false + } +} + +pub(in crate::fmt) use self::imp::*; diff --git a/vendor/env_logger/src/fmt/writer/mod.rs b/vendor/env_logger/src/fmt/writer/mod.rs new file mode 100644 index 000000000..7f4b6f949 --- /dev/null +++ b/vendor/env_logger/src/fmt/writer/mod.rs @@ -0,0 +1,253 @@ +mod atty; +mod termcolor; + +use self::atty::{is_stderr, is_stdout}; +use self::termcolor::BufferWriter; +use std::{fmt, io, mem, sync::Mutex}; + +pub(super) mod glob { + pub use super::termcolor::glob::*; + pub use super::*; +} + +pub(super) use self::termcolor::Buffer; + +/// Log target, either `stdout`, `stderr` or a custom pipe. +#[non_exhaustive] +pub enum Target { + /// Logs will be sent to standard output. + Stdout, + /// Logs will be sent to standard error. + Stderr, + /// Logs will be sent to a custom pipe. + Pipe(Box), +} + +impl Default for Target { + fn default() -> Self { + Target::Stderr + } +} + +impl fmt::Debug for Target { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Stdout => "stdout", + Self::Stderr => "stderr", + Self::Pipe(_) => "pipe", + } + ) + } +} + +/// Log target, either `stdout`, `stderr` or a custom pipe. +/// +/// Same as `Target`, except the pipe is wrapped in a mutex for interior mutability. +pub(super) enum WritableTarget { + /// Logs will be sent to standard output. + Stdout, + /// Logs will be sent to standard error. + Stderr, + /// Logs will be sent to a custom pipe. + Pipe(Box>), +} + +impl From for WritableTarget { + fn from(target: Target) -> Self { + match target { + Target::Stdout => Self::Stdout, + Target::Stderr => Self::Stderr, + Target::Pipe(pipe) => Self::Pipe(Box::new(Mutex::new(pipe))), + } + } +} + +impl Default for WritableTarget { + fn default() -> Self { + Self::from(Target::default()) + } +} + +impl fmt::Debug for WritableTarget { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Stdout => "stdout", + Self::Stderr => "stderr", + Self::Pipe(_) => "pipe", + } + ) + } +} +/// Whether or not to print styles to the target. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum WriteStyle { + /// Try to print styles, but don't force the issue. + Auto, + /// Try very hard to print styles. + Always, + /// Never print styles. + Never, +} + +impl Default for WriteStyle { + fn default() -> Self { + WriteStyle::Auto + } +} + +/// A terminal target with color awareness. +pub(crate) struct Writer { + inner: BufferWriter, + write_style: WriteStyle, +} + +impl Writer { + pub fn write_style(&self) -> WriteStyle { + self.write_style + } + + pub(super) fn buffer(&self) -> Buffer { + self.inner.buffer() + } + + pub(super) fn print(&self, buf: &Buffer) -> io::Result<()> { + self.inner.print(buf) + } +} + +/// A builder for a terminal writer. +/// +/// The target and style choice can be configured before building. +#[derive(Debug)] +pub(crate) struct Builder { + target: WritableTarget, + write_style: WriteStyle, + is_test: bool, + built: bool, +} + +impl Builder { + /// Initialize the writer builder with defaults. + pub(crate) fn new() -> Self { + Builder { + target: Default::default(), + write_style: Default::default(), + is_test: false, + built: false, + } + } + + /// Set the target to write to. + pub(crate) fn target(&mut self, target: Target) -> &mut Self { + self.target = target.into(); + self + } + + /// Parses a style choice string. + /// + /// See the [Disabling colors] section for more details. + /// + /// [Disabling colors]: ../index.html#disabling-colors + pub(crate) fn parse_write_style(&mut self, write_style: &str) -> &mut Self { + self.write_style(parse_write_style(write_style)) + } + + /// Whether or not to print style characters when writing. + pub(crate) fn write_style(&mut self, write_style: WriteStyle) -> &mut Self { + self.write_style = write_style; + self + } + + /// Whether or not to capture logs for `cargo test`. + #[allow(clippy::wrong_self_convention)] + pub(crate) fn is_test(&mut self, is_test: bool) -> &mut Self { + self.is_test = is_test; + self + } + + /// Build a terminal writer. + pub(crate) fn build(&mut self) -> Writer { + assert!(!self.built, "attempt to re-use consumed builder"); + self.built = true; + + let color_choice = match self.write_style { + WriteStyle::Auto => { + if match &self.target { + WritableTarget::Stderr => is_stderr(), + WritableTarget::Stdout => is_stdout(), + WritableTarget::Pipe(_) => false, + } { + WriteStyle::Auto + } else { + WriteStyle::Never + } + } + color_choice => color_choice, + }; + + let writer = match mem::take(&mut self.target) { + WritableTarget::Stderr => BufferWriter::stderr(self.is_test, color_choice), + WritableTarget::Stdout => BufferWriter::stdout(self.is_test, color_choice), + WritableTarget::Pipe(pipe) => BufferWriter::pipe(color_choice, pipe), + }; + + Writer { + inner: writer, + write_style: self.write_style, + } + } +} + +impl Default for Builder { + fn default() -> Self { + Builder::new() + } +} + +impl fmt::Debug for Writer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Writer").finish() + } +} + +fn parse_write_style(spec: &str) -> WriteStyle { + match spec { + "auto" => WriteStyle::Auto, + "always" => WriteStyle::Always, + "never" => WriteStyle::Never, + _ => Default::default(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_write_style_valid() { + let inputs = vec![ + ("auto", WriteStyle::Auto), + ("always", WriteStyle::Always), + ("never", WriteStyle::Never), + ]; + + for (input, expected) in inputs { + assert_eq!(expected, parse_write_style(input)); + } + } + + #[test] + fn parse_write_style_invalid() { + let inputs = vec!["", "true", "false", "NEVER!!"]; + + for input in inputs { + assert_eq!(WriteStyle::Auto, parse_write_style(input)); + } + } +} diff --git a/vendor/env_logger/src/fmt/writer/termcolor/extern_impl.rs b/vendor/env_logger/src/fmt/writer/termcolor/extern_impl.rs new file mode 100644 index 000000000..89c382239 --- /dev/null +++ b/vendor/env_logger/src/fmt/writer/termcolor/extern_impl.rs @@ -0,0 +1,532 @@ +use std::borrow::Cow; +use std::cell::RefCell; +use std::fmt; +use std::io::{self, Write}; +use std::rc::Rc; +use std::sync::Mutex; + +use log::Level; +use termcolor::{self, ColorChoice, ColorSpec, WriteColor}; + +use crate::fmt::{Formatter, WritableTarget, WriteStyle}; + +pub(in crate::fmt::writer) mod glob { + pub use super::*; +} + +impl Formatter { + /// Begin a new [`Style`]. + /// + /// # Examples + /// + /// Create a bold, red colored style and use it to print the log level: + /// + /// ``` + /// use std::io::Write; + /// use env_logger::fmt::Color; + /// + /// let mut builder = env_logger::Builder::new(); + /// + /// builder.format(|buf, record| { + /// let mut level_style = buf.style(); + /// + /// level_style.set_color(Color::Red).set_bold(true); + /// + /// writeln!(buf, "{}: {}", + /// level_style.value(record.level()), + /// record.args()) + /// }); + /// ``` + /// + /// [`Style`]: struct.Style.html + pub fn style(&self) -> Style { + Style { + buf: self.buf.clone(), + spec: ColorSpec::new(), + } + } + + /// Get the default [`Style`] for the given level. + /// + /// The style can be used to print other values besides the level. + pub fn default_level_style(&self, level: Level) -> Style { + let mut level_style = self.style(); + match level { + Level::Trace => level_style.set_color(Color::Cyan), + Level::Debug => level_style.set_color(Color::Blue), + Level::Info => level_style.set_color(Color::Green), + Level::Warn => level_style.set_color(Color::Yellow), + Level::Error => level_style.set_color(Color::Red).set_bold(true), + }; + level_style + } + + /// Get a printable [`Style`] for the given level. + /// + /// The style can only be used to print the level. + pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> { + self.default_level_style(level).into_value(level) + } +} + +pub(in crate::fmt::writer) struct BufferWriter { + inner: termcolor::BufferWriter, + uncolored_target: Option, +} + +pub(in crate::fmt) struct Buffer { + inner: termcolor::Buffer, + has_uncolored_target: bool, +} + +impl BufferWriter { + pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self { + BufferWriter { + inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), + uncolored_target: if is_test { + Some(WritableTarget::Stderr) + } else { + None + }, + } + } + + pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self { + BufferWriter { + inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()), + uncolored_target: if is_test { + Some(WritableTarget::Stdout) + } else { + None + }, + } + } + + pub(in crate::fmt::writer) fn pipe( + write_style: WriteStyle, + pipe: Box>, + ) -> Self { + BufferWriter { + // The inner Buffer is never printed from, but it is still needed to handle coloring and other formatting + inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), + uncolored_target: Some(WritableTarget::Pipe(pipe)), + } + } + + pub(in crate::fmt::writer) fn buffer(&self) -> Buffer { + Buffer { + inner: self.inner.buffer(), + has_uncolored_target: self.uncolored_target.is_some(), + } + } + + pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { + if let Some(target) = &self.uncolored_target { + // This impl uses the `eprint` and `print` macros + // instead of `termcolor`'s buffer. + // This is so their output can be captured by `cargo test` + let log = String::from_utf8_lossy(buf.bytes()); + + match target { + WritableTarget::Stderr => eprint!("{}", log), + WritableTarget::Stdout => print!("{}", log), + WritableTarget::Pipe(pipe) => write!(pipe.lock().unwrap(), "{}", log)?, + } + + Ok(()) + } else { + self.inner.print(&buf.inner) + } + } +} + +impl Buffer { + pub(in crate::fmt) fn clear(&mut self) { + self.inner.clear() + } + + pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.write(buf) + } + + pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } + + pub(in crate::fmt) fn bytes(&self) -> &[u8] { + self.inner.as_slice() + } + + fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { + // Ignore styles for test captured logs because they can't be printed + if !self.has_uncolored_target { + self.inner.set_color(spec) + } else { + Ok(()) + } + } + + fn reset(&mut self) -> io::Result<()> { + // Ignore styles for test captured logs because they can't be printed + if !self.has_uncolored_target { + self.inner.reset() + } else { + Ok(()) + } + } +} + +impl WriteStyle { + fn into_color_choice(self) -> ColorChoice { + match self { + WriteStyle::Always => ColorChoice::Always, + WriteStyle::Auto => ColorChoice::Auto, + WriteStyle::Never => ColorChoice::Never, + } + } +} + +/// A set of styles to apply to the terminal output. +/// +/// Call [`Formatter::style`] to get a `Style` and use the builder methods to +/// set styling properties, like [color] and [weight]. +/// To print a value using the style, wrap it in a call to [`value`] when the log +/// record is formatted. +/// +/// # Examples +/// +/// Create a bold, red colored style and use it to print the log level: +/// +/// ``` +/// use std::io::Write; +/// use env_logger::fmt::Color; +/// +/// let mut builder = env_logger::Builder::new(); +/// +/// builder.format(|buf, record| { +/// let mut level_style = buf.style(); +/// +/// level_style.set_color(Color::Red).set_bold(true); +/// +/// writeln!(buf, "{}: {}", +/// level_style.value(record.level()), +/// record.args()) +/// }); +/// ``` +/// +/// Styles can be re-used to output multiple values: +/// +/// ``` +/// use std::io::Write; +/// use env_logger::fmt::Color; +/// +/// let mut builder = env_logger::Builder::new(); +/// +/// builder.format(|buf, record| { +/// let mut bold = buf.style(); +/// +/// bold.set_bold(true); +/// +/// writeln!(buf, "{}: {} {}", +/// bold.value(record.level()), +/// bold.value("some bold text"), +/// record.args()) +/// }); +/// ``` +/// +/// [`Formatter::style`]: struct.Formatter.html#method.style +/// [color]: #method.set_color +/// [weight]: #method.set_bold +/// [`value`]: #method.value +#[derive(Clone)] +pub struct Style { + buf: Rc>, + spec: ColorSpec, +} + +/// A value that can be printed using the given styles. +/// +/// It is the result of calling [`Style::value`]. +/// +/// [`Style::value`]: struct.Style.html#method.value +pub struct StyledValue<'a, T> { + style: Cow<'a, Style>, + value: T, +} + +impl Style { + /// Set the text color. + /// + /// # Examples + /// + /// Create a style with red text: + /// + /// ``` + /// use std::io::Write; + /// use env_logger::fmt::Color; + /// + /// let mut builder = env_logger::Builder::new(); + /// + /// builder.format(|buf, record| { + /// let mut style = buf.style(); + /// + /// style.set_color(Color::Red); + /// + /// writeln!(buf, "{}", style.value(record.args())) + /// }); + /// ``` + pub fn set_color(&mut self, color: Color) -> &mut Style { + self.spec.set_fg(Some(color.into_termcolor())); + self + } + + /// Set the text weight. + /// + /// If `yes` is true then text will be written in bold. + /// If `yes` is false then text will be written in the default weight. + /// + /// # Examples + /// + /// Create a style with bold text: + /// + /// ``` + /// use std::io::Write; + /// + /// let mut builder = env_logger::Builder::new(); + /// + /// builder.format(|buf, record| { + /// let mut style = buf.style(); + /// + /// style.set_bold(true); + /// + /// writeln!(buf, "{}", style.value(record.args())) + /// }); + /// ``` + pub fn set_bold(&mut self, yes: bool) -> &mut Style { + self.spec.set_bold(yes); + self + } + + /// Set the text intensity. + /// + /// If `yes` is true then text will be written in a brighter color. + /// If `yes` is false then text will be written in the default color. + /// + /// # Examples + /// + /// Create a style with intense text: + /// + /// ``` + /// use std::io::Write; + /// + /// let mut builder = env_logger::Builder::new(); + /// + /// builder.format(|buf, record| { + /// let mut style = buf.style(); + /// + /// style.set_intense(true); + /// + /// writeln!(buf, "{}", style.value(record.args())) + /// }); + /// ``` + pub fn set_intense(&mut self, yes: bool) -> &mut Style { + self.spec.set_intense(yes); + self + } + + /// Set whether the text is dimmed. + /// + /// If `yes` is true then text will be written in a dimmer color. + /// If `yes` is false then text will be written in the default color. + /// + /// # Examples + /// + /// Create a style with dimmed text: + /// + /// ``` + /// use std::io::Write; + /// + /// let mut builder = env_logger::Builder::new(); + /// + /// builder.format(|buf, record| { + /// let mut style = buf.style(); + /// + /// style.set_dimmed(true); + /// + /// writeln!(buf, "{}", style.value(record.args())) + /// }); + /// ``` + pub fn set_dimmed(&mut self, yes: bool) -> &mut Style { + self.spec.set_dimmed(yes); + self + } + + /// Set the background color. + /// + /// # Examples + /// + /// Create a style with a yellow background: + /// + /// ``` + /// use std::io::Write; + /// use env_logger::fmt::Color; + /// + /// let mut builder = env_logger::Builder::new(); + /// + /// builder.format(|buf, record| { + /// let mut style = buf.style(); + /// + /// style.set_bg(Color::Yellow); + /// + /// writeln!(buf, "{}", style.value(record.args())) + /// }); + /// ``` + pub fn set_bg(&mut self, color: Color) -> &mut Style { + self.spec.set_bg(Some(color.into_termcolor())); + self + } + + /// Wrap a value in the style. + /// + /// The same `Style` can be used to print multiple different values. + /// + /// # Examples + /// + /// Create a bold, red colored style and use it to print the log level: + /// + /// ``` + /// use std::io::Write; + /// use env_logger::fmt::Color; + /// + /// let mut builder = env_logger::Builder::new(); + /// + /// builder.format(|buf, record| { + /// let mut style = buf.style(); + /// + /// style.set_color(Color::Red).set_bold(true); + /// + /// writeln!(buf, "{}: {}", + /// style.value(record.level()), + /// record.args()) + /// }); + /// ``` + pub fn value(&self, value: T) -> StyledValue { + StyledValue { + style: Cow::Borrowed(self), + value, + } + } + + /// Wrap a value in the style by taking ownership of it. + pub(crate) fn into_value(self, value: T) -> StyledValue<'static, T> { + StyledValue { + style: Cow::Owned(self), + value, + } + } +} + +impl<'a, T> StyledValue<'a, T> { + fn write_fmt(&self, f: F) -> fmt::Result + where + F: FnOnce() -> fmt::Result, + { + self.style + .buf + .borrow_mut() + .set_color(&self.style.spec) + .map_err(|_| fmt::Error)?; + + // Always try to reset the terminal style, even if writing failed + let write = f(); + let reset = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error); + + write.and(reset) + } +} + +impl fmt::Debug for Style { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Style").field("spec", &self.spec).finish() + } +} + +macro_rules! impl_styled_value_fmt { + ($($fmt_trait:path),*) => { + $( + impl<'a, T: $fmt_trait> $fmt_trait for StyledValue<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { + self.write_fmt(|| T::fmt(&self.value, f)) + } + } + )* + }; +} + +impl_styled_value_fmt!( + fmt::Debug, + fmt::Display, + fmt::Pointer, + fmt::Octal, + fmt::Binary, + fmt::UpperHex, + fmt::LowerHex, + fmt::UpperExp, + fmt::LowerExp +); + +// The `Color` type is copied from https://github.com/BurntSushi/termcolor + +/// The set of available colors for the terminal foreground/background. +/// +/// The `Ansi256` and `Rgb` colors will only output the correct codes when +/// paired with the `Ansi` `WriteColor` implementation. +/// +/// The `Ansi256` and `Rgb` color types are not supported when writing colors +/// on Windows using the console. If they are used on Windows, then they are +/// silently ignored and no colors will be emitted. +/// +/// This set may expand over time. +/// +/// This type has a `FromStr` impl that can parse colors from their human +/// readable form. The format is as follows: +/// +/// 1. Any of the explicitly listed colors in English. They are matched +/// case insensitively. +/// 2. A single 8-bit integer, in either decimal or hexadecimal format. +/// 3. A triple of 8-bit integers separated by a comma, where each integer is +/// in decimal or hexadecimal format. +/// +/// Hexadecimal numbers are written with a `0x` prefix. +#[allow(missing_docs)] +#[non_exhaustive] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Color { + Black, + Blue, + Green, + Red, + Cyan, + Magenta, + Yellow, + White, + Ansi256(u8), + Rgb(u8, u8, u8), +} + +impl Color { + fn into_termcolor(self) -> termcolor::Color { + match self { + Color::Black => termcolor::Color::Black, + Color::Blue => termcolor::Color::Blue, + Color::Green => termcolor::Color::Green, + Color::Red => termcolor::Color::Red, + Color::Cyan => termcolor::Color::Cyan, + Color::Magenta => termcolor::Color::Magenta, + Color::Yellow => termcolor::Color::Yellow, + Color::White => termcolor::Color::White, + Color::Ansi256(value) => termcolor::Color::Ansi256(value), + Color::Rgb(r, g, b) => termcolor::Color::Rgb(r, g, b), + } + } +} diff --git a/vendor/env_logger/src/fmt/writer/termcolor/mod.rs b/vendor/env_logger/src/fmt/writer/termcolor/mod.rs new file mode 100644 index 000000000..20f01979e --- /dev/null +++ b/vendor/env_logger/src/fmt/writer/termcolor/mod.rs @@ -0,0 +1,12 @@ +/* +This internal module contains the style and terminal writing implementation. + +Its public API is available when the `termcolor` crate is available. +The terminal printing is shimmed when the `termcolor` crate is not available. +*/ + +#[cfg_attr(feature = "color", path = "extern_impl.rs")] +#[cfg_attr(not(feature = "color"), path = "shim_impl.rs")] +mod imp; + +pub(in crate::fmt) use self::imp::*; diff --git a/vendor/env_logger/src/fmt/writer/termcolor/shim_impl.rs b/vendor/env_logger/src/fmt/writer/termcolor/shim_impl.rs new file mode 100644 index 000000000..0705770c9 --- /dev/null +++ b/vendor/env_logger/src/fmt/writer/termcolor/shim_impl.rs @@ -0,0 +1,72 @@ +use std::{io, sync::Mutex}; + +use crate::fmt::{WritableTarget, WriteStyle}; + +pub(in crate::fmt::writer) mod glob {} + +pub(in crate::fmt::writer) struct BufferWriter { + target: WritableTarget, +} + +pub(in crate::fmt) struct Buffer(Vec); + +impl BufferWriter { + pub(in crate::fmt::writer) fn stderr(_is_test: bool, _write_style: WriteStyle) -> Self { + BufferWriter { + target: WritableTarget::Stderr, + } + } + + pub(in crate::fmt::writer) fn stdout(_is_test: bool, _write_style: WriteStyle) -> Self { + BufferWriter { + target: WritableTarget::Stdout, + } + } + + pub(in crate::fmt::writer) fn pipe( + _write_style: WriteStyle, + pipe: Box>, + ) -> Self { + BufferWriter { + target: WritableTarget::Pipe(pipe), + } + } + + pub(in crate::fmt::writer) fn buffer(&self) -> Buffer { + Buffer(Vec::new()) + } + + pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { + // This impl uses the `eprint` and `print` macros + // instead of using the streams directly. + // This is so their output can be captured by `cargo test`. + match &self.target { + // Safety: If the target type is `Pipe`, `target_pipe` will always be non-empty. + WritableTarget::Pipe(pipe) => pipe.lock().unwrap().write_all(&buf.0)?, + WritableTarget::Stdout => print!("{}", String::from_utf8_lossy(&buf.0)), + WritableTarget::Stderr => eprint!("{}", String::from_utf8_lossy(&buf.0)), + } + + Ok(()) + } +} + +impl Buffer { + pub(in crate::fmt) fn clear(&mut self) { + self.0.clear(); + } + + pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.extend(buf); + Ok(buf.len()) + } + + pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + #[cfg(test)] + pub(in crate::fmt) fn bytes(&self) -> &[u8] { + &self.0 + } +} -- cgit v1.2.3