use std::fmt;
use std::io::{self, Write};
use crate::termcolor::{self, ColorSpec, StandardStream, WriteColor};
pub(super) enum Stream {
Stdout,
Stderr,
}
impl Stream {
fn find<'a>(
&self,
stdout: &'a mut StandardStream,
stderr: &'a mut StandardStream,
) -> &'a mut StandardStream {
match self {
Stream::Stdout => stdout,
Stream::Stderr => stderr,
}
}
}
pub(super) enum Color {
Error,
Passed,
Ignore,
Highlight,
Important,
Progress,
}
impl Color {
fn find<'a>(&self, colors: &'a Colors) -> &'a ColorSpec {
match self {
Color::Error => &colors.error,
Color::Passed => &colors.passed,
Color::Ignore => &colors.ignored,
Color::Highlight => &colors.highlight,
Color::Important => &colors.important,
Color::Progress => &colors.progress,
}
}
}
pub(super) struct Io<'io> {
pub(super) stdout: &'io mut StandardStream,
pub(super) stderr: &'io mut StandardStream,
colors: Option<Colors>,
}
impl<'io> Io<'io> {
pub(super) fn new(stdout: &'io mut StandardStream, stderr: &'io mut StandardStream) -> Self {
Self {
stdout,
stderr,
colors: None,
}
}
pub(super) fn with_color(
&mut self,
stream: Stream,
color: Color,
) -> io::Result<&mut ColorStream> {
let stream = stream.find(self.stdout, self.stderr);
let colors = self.colors.get_or_insert_with(Colors::new);
stream.set_color(color.find(colors))?;
Ok(ColorStream::new(stream))
}
pub(super) fn section(
&mut self,
title: impl fmt::Display,
stream: Stream,
color: Color,
) -> io::Result<Section<'_>> {
let io = stream.find(self.stdout, self.stderr);
let colors = self.colors.get_or_insert_with(Colors::new);
io.set_color(color.find(colors))?;
write!(io, "{title:>12}")?;
io.reset()?;
Ok(Section { io, colors })
}
pub(super) fn write(
&mut self,
title: impl fmt::Display,
stream: Stream,
color: Color,
) -> io::Result<()> {
let stream = stream.find(self.stdout, self.stderr);
let colors = self.colors.get_or_insert_with(Colors::new);
stream.set_color(color.find(colors))?;
write!(stream, "{title}")?;
stream.reset()?;
Ok(())
}
}
#[derive(Default)]
struct Colors {
error: ColorSpec,
passed: ColorSpec,
highlight: ColorSpec,
important: ColorSpec,
progress: ColorSpec,
ignored: ColorSpec,
}
impl Colors {
fn new() -> Self {
let mut this = Self::default();
this.error.set_fg(Some(termcolor::Color::Red));
this.passed.set_fg(Some(termcolor::Color::Green));
this.highlight.set_fg(Some(termcolor::Color::Green));
this.highlight.set_bold(true);
this.important.set_fg(Some(termcolor::Color::White));
this.important.set_bold(true);
this.progress.set_fg(Some(termcolor::Color::Cyan));
this.progress.set_bold(true);
this.ignored.set_fg(Some(termcolor::Color::Yellow));
this.ignored.set_bold(true);
this
}
}
pub(super) struct Section<'a> {
pub(super) io: &'a mut StandardStream,
colors: &'a Colors,
}
impl Section<'_> {
pub(super) fn append(&mut self, text: impl fmt::Display) -> io::Result<&mut Self> {
write!(self.io, "{text}")?;
Ok(self)
}
pub(super) fn flush(&mut self) -> io::Result<&mut Self> {
self.io.flush()?;
Ok(self)
}
pub(super) fn append_with(
&mut self,
text: impl fmt::Display,
color: Color,
) -> io::Result<&mut Self> {
self.io.set_color(color.find(self.colors))?;
write!(self.io, "{text}")?;
self.io.reset()?;
Ok(self)
}
pub(super) fn error(&mut self, text: impl fmt::Display) -> io::Result<&mut Self> {
self.append_with(text, Color::Error)?;
Ok(self)
}
pub(super) fn passed(&mut self, text: impl fmt::Display) -> io::Result<&mut Self> {
self.append_with(text, Color::Passed)?;
Ok(self)
}
pub(super) fn close(&mut self) -> io::Result<()> {
writeln!(self.io)?;
Ok(())
}
}
#[repr(transparent)]
pub(super) struct ColorStream(StandardStream);
impl ColorStream {
fn new(io: &mut StandardStream) -> &mut Self {
unsafe { &mut *(io as *mut StandardStream as *mut Self) }
}
pub(super) fn close(&mut self) -> io::Result<()> {
self.0.reset()
}
}
impl io::Write for ColorStream {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}