blob: 6ee63a39811c1d979b4274019a8563b6b8c81163 [file] [log] [blame]
mod atty;
mod termcolor;
use self::atty::{is_stderr, is_stdout};
use self::termcolor::BufferWriter;
use std::{fmt, io};
pub(in crate::fmt) mod glob {
pub use super::termcolor::glob::*;
pub use super::*;
}
pub(in crate::fmt) use self::termcolor::Buffer;
/// Log target, either `stdout` or `stderr`.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Target {
/// Logs will be sent to standard output.
Stdout,
/// Logs will be sent to standard error.
Stderr,
}
impl Default for Target {
fn default() -> Self {
Target::Stderr
}
}
/// 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(in crate::fmt) fn buffer(&self) -> Buffer {
self.inner.buffer()
}
pub(in crate::fmt) 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.
pub(crate) struct Builder {
target: Target,
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;
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`.
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 {
Target::Stderr => is_stderr(),
Target::Stdout => is_stdout(),
} {
WriteStyle::Auto
} else {
WriteStyle::Never
}
}
color_choice => color_choice,
};
let writer = match self.target {
Target::Stderr => BufferWriter::stderr(self.is_test, color_choice),
Target::Stdout => BufferWriter::stdout(self.is_test, color_choice),
};
Writer {
inner: writer,
write_style: self.write_style,
}
}
}
impl Default for Builder {
fn default() -> Self {
Builder::new()
}
}
impl fmt::Debug for Builder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Logger")
.field("target", &self.target)
.field("write_style", &self.write_style)
.finish()
}
}
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));
}
}
}