mod buffer;
mod target;
use self::buffer::BufferWriter;
use std::{io, mem, sync::Mutex};
pub(super) use self::buffer::Buffer;
pub use target::Target;
#[allow(clippy::exhaustive_enums)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)]
pub enum WriteStyle {
#[default]
Auto,
Always,
Never,
}
#[cfg(feature = "color")]
impl From<anstream::ColorChoice> for WriteStyle {
fn from(choice: anstream::ColorChoice) -> Self {
match choice {
anstream::ColorChoice::Auto => Self::Auto,
anstream::ColorChoice::Always => Self::Always,
anstream::ColorChoice::AlwaysAnsi => Self::Always,
anstream::ColorChoice::Never => Self::Never,
}
}
}
#[cfg(feature = "color")]
impl From<WriteStyle> for anstream::ColorChoice {
fn from(choice: WriteStyle) -> Self {
match choice {
WriteStyle::Auto => anstream::ColorChoice::Auto,
WriteStyle::Always => anstream::ColorChoice::Always,
WriteStyle::Never => anstream::ColorChoice::Never,
}
}
}
#[derive(Debug)]
pub(crate) struct Writer {
inner: BufferWriter,
}
impl Writer {
pub(crate) fn write_style(&self) -> WriteStyle {
self.inner.write_style()
}
pub(super) fn buffer(&self) -> Buffer {
self.inner.buffer()
}
pub(super) fn print(&self, buf: &Buffer) -> io::Result<()> {
self.inner.print(buf)
}
}
#[derive(Debug)]
pub(crate) struct Builder {
target: Target,
write_style: WriteStyle,
is_test: bool,
built: bool,
}
impl Builder {
pub(crate) fn new() -> Self {
Builder {
target: Default::default(),
write_style: Default::default(),
is_test: false,
built: false,
}
}
pub(crate) fn target(&mut self, target: Target) -> &mut Self {
self.target = target;
self
}
pub(crate) fn parse_write_style(&mut self, write_style: &str) -> &mut Self {
self.write_style(parse_write_style(write_style))
}
pub(crate) fn write_style(&mut self, write_style: WriteStyle) -> &mut Self {
self.write_style = write_style;
self
}
#[allow(clippy::wrong_self_convention)]
pub(crate) fn is_test(&mut self, is_test: bool) -> &mut Self {
self.is_test = is_test;
self
}
pub(crate) fn build(&mut self) -> Writer {
assert!(!self.built, "attempt to re-use consumed builder");
self.built = true;
let color_choice = self.write_style;
#[cfg(feature = "auto-color")]
let color_choice = if color_choice == WriteStyle::Auto {
match &self.target {
Target::Stdout => anstream::AutoStream::choice(&io::stdout()).into(),
Target::Stderr => anstream::AutoStream::choice(&io::stderr()).into(),
Target::Pipe(_) => color_choice,
}
} else {
color_choice
};
let color_choice = if color_choice == WriteStyle::Auto {
WriteStyle::Never
} else {
color_choice
};
let writer = match mem::take(&mut self.target) {
Target::Stdout => BufferWriter::stdout(self.is_test, color_choice),
Target::Stderr => BufferWriter::stderr(self.is_test, color_choice),
Target::Pipe(pipe) => BufferWriter::pipe(Box::new(Mutex::new(pipe)), color_choice),
};
Writer { inner: writer }
}
}
impl Default for Builder {
fn default() -> Self {
Builder::new()
}
}
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));
}
}
}