use std::io::Write;
use std::io::{self};
use std::sync::Arc;
use std::sync::Mutex;
#[derive(strum::Display, strum::EnumString, strum::VariantNames)]
#[strum(serialize_all = "kebab-case")]
#[derive(Default)]
pub enum ReportingTarget {
#[strum(serialize = "stdout", serialize = "out")]
#[default]
Stdout,
#[strum(serialize = "stderr", serialize = "err")]
Stderr,
#[strum(disabled)]
Writer(Arc<Mutex<Box<dyn Write + Send>>>),
}
impl Clone for ReportingTarget {
fn clone(&self) -> Self {
match self {
ReportingTarget::Stdout => ReportingTarget::Stdout,
ReportingTarget::Stderr => ReportingTarget::Stderr,
ReportingTarget::Writer(writer) => ReportingTarget::Writer(writer.clone()),
}
}
}
impl std::fmt::Debug for ReportingTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ReportingTarget::Stdout => write!(f, "ReportingTarget::Stdout"),
ReportingTarget::Stderr => write!(f, "ReportingTarget::Stderr"),
ReportingTarget::Writer(_) => write!(f, "ReportingTarget::Writer(..)"),
}
}
}
impl ReportingTarget {
#[must_use]
pub fn buffer() -> (Self, Arc<Mutex<Vec<u8>>>) {
let buffer = Arc::new(Mutex::new(Vec::new()));
let writer_buffer = buffer.clone();
let target = ReportingTarget::Writer(Arc::new(Mutex::new(Box::new(BufferWriter { buffer: writer_buffer }))));
(target, buffer)
}
pub(crate) fn resolve(&self) -> Box<dyn Write + '_> {
match self {
ReportingTarget::Stdout => Box::new(io::stdout()),
ReportingTarget::Stderr => Box::new(io::stderr()),
ReportingTarget::Writer(writer) => Box::new(LockedWriter { writer: writer.clone() }),
}
}
}
struct LockedWriter {
writer: Arc<Mutex<Box<dyn Write + Send>>>,
}
impl Write for LockedWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.writer.lock().unwrap().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.writer.lock().unwrap().flush()
}
}
struct BufferWriter {
buffer: Arc<Mutex<Vec<u8>>>,
}
impl Write for BufferWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buffer.lock().unwrap().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.buffer.lock().unwrap().flush()
}
}