use io::Write;
use std::{fmt::Debug, io};
pub trait MakeWriter {
type Writer: io::Write;
fn make_writer(&self) -> Self::Writer;
}
impl<F, W> MakeWriter for F
where
F: Fn() -> W,
W: io::Write,
{
type Writer = W;
fn make_writer(&self) -> Self::Writer {
(self)()
}
}
#[derive(Default, Debug)]
pub struct TestWriter {
_p: (),
}
impl TestWriter {
pub fn new() -> Self {
Self::default()
}
}
impl io::Write for TestWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let out_str = String::from_utf8_lossy(buf);
print!("{}", out_str);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl MakeWriter for TestWriter {
type Writer = Self;
fn make_writer(&self) -> Self::Writer {
Self::default()
}
}
pub struct BoxMakeWriter {
inner: Box<dyn MakeWriter<Writer = Box<dyn Write>> + Send + Sync>,
}
impl BoxMakeWriter {
pub fn new<M>(make_writer: M) -> Self
where
M: MakeWriter + Send + Sync + 'static,
M::Writer: Write + 'static,
{
Self {
inner: Box::new(Boxed(make_writer)),
}
}
}
impl Debug for BoxMakeWriter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad("BoxMakeWriter { ... }")
}
}
impl MakeWriter for BoxMakeWriter {
type Writer = Box<dyn Write>;
fn make_writer(&self) -> Self::Writer {
self.inner.make_writer()
}
}
struct Boxed<M>(M);
impl<M> MakeWriter for Boxed<M>
where
M: MakeWriter,
M::Writer: Write + 'static,
{
type Writer = Box<dyn Write>;
fn make_writer(&self) -> Self::Writer {
Box::new(self.0.make_writer())
}
}
#[cfg(test)]
mod test {
use super::MakeWriter;
use crate::fmt::format::Format;
use crate::fmt::test::{MockMakeWriter, MockWriter};
use crate::fmt::Subscriber;
use lazy_static::lazy_static;
use std::sync::Mutex;
use tracing::error;
use tracing_core::dispatcher::{self, Dispatch};
fn test_writer<T>(make_writer: T, msg: &str, buf: &Mutex<Vec<u8>>)
where
T: MakeWriter + Send + Sync + 'static,
{
let subscriber = {
#[cfg(feature = "ansi")]
{
let f = Format::default().without_time().with_ansi(false);
Subscriber::builder()
.event_format(f)
.with_writer(make_writer)
.finish()
}
#[cfg(not(feature = "ansi"))]
{
let f = Format::default().without_time();
Subscriber::builder()
.event_format(f)
.with_writer(make_writer)
.finish()
}
};
let dispatch = Dispatch::from(subscriber);
dispatcher::with_default(&dispatch, || {
error!("{}", msg);
});
let expected = format!("ERROR {}: {}\n", module_path!(), msg);
let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap();
assert!(actual.contains(expected.as_str()));
}
#[test]
fn custom_writer_closure() {
lazy_static! {
static ref BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
}
let make_writer = || MockWriter::new(&BUF);
let msg = "my custom writer closure error";
test_writer(make_writer, msg, &BUF);
}
#[test]
fn custom_writer_struct() {
lazy_static! {
static ref BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
}
let make_writer = MockMakeWriter::new(&BUF);
let msg = "my custom writer struct error";
test_writer(make_writer, msg, &BUF);
}
}