use crate::processor::{self, Processor};
use crate::tree::Tree;
use std::error::Error;
use std::io::{self, Write};
use tracing_subscriber::fmt::MakeWriter;
mod pretty;
pub use pretty::Pretty;
pub trait Formatter {
type Error: Error + Send + Sync;
fn fmt(&self, tree: &Tree) -> Result<String, Self::Error>;
}
impl<F, E> Formatter for F
where
F: Fn(&Tree) -> Result<String, E>,
E: Error + Send + Sync,
{
type Error = E;
#[inline]
fn fmt(&self, tree: &Tree) -> Result<String, E> {
self(tree)
}
}
#[derive(Clone, Debug)]
pub struct Printer<F, W> {
formatter: F,
make_writer: W,
}
#[derive(Debug)]
pub struct MakeStdout;
#[derive(Debug)]
pub struct MakeStderr;
impl MakeWriter<'_> for MakeStdout {
type Writer = io::Stdout;
fn make_writer(&self) -> Self::Writer {
io::stdout()
}
}
impl MakeWriter<'_> for MakeStderr {
type Writer = io::Stderr;
fn make_writer(&self) -> Self::Writer {
io::stderr()
}
}
pub type PrettyPrinter = Printer<Pretty, MakeStdout>;
impl PrettyPrinter {
pub const fn new() -> Self {
Printer {
formatter: Pretty,
make_writer: MakeStdout,
}
}
}
impl<F, W> Printer<F, W>
where
F: 'static + Formatter,
W: 'static + for<'a> MakeWriter<'a>,
{
pub fn formatter<F2>(self, formatter: F2) -> Printer<F2, W>
where
F2: 'static + Formatter,
{
Printer {
formatter,
make_writer: self.make_writer,
}
}
pub fn writer<W2>(self, make_writer: W2) -> Printer<F, W2>
where
W2: 'static + for<'a> MakeWriter<'a>,
{
Printer {
formatter: self.formatter,
make_writer,
}
}
}
impl Default for PrettyPrinter {
fn default() -> Self {
PrettyPrinter::new()
}
}
impl<F, W> Processor for Printer<F, W>
where
F: 'static + Formatter,
W: 'static + for<'a> MakeWriter<'a>,
{
fn process(&self, tree: Tree) -> processor::Result {
let string = match self.formatter.fmt(&tree) {
Ok(s) => s,
Err(e) => return Err(processor::error(tree, e.into())),
};
match self.make_writer.make_writer().write_all(string.as_bytes()) {
Ok(()) => Ok(()),
Err(e) => Err(processor::error(tree, e.into())),
}
}
}
#[derive(Clone, Debug)]
pub struct TestCapturePrinter<F> {
formatter: F,
}
impl TestCapturePrinter<Pretty> {
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
TestCapturePrinter { formatter: Pretty }
}
}
impl<F> Processor for TestCapturePrinter<F>
where
F: 'static + Formatter,
{
fn process(&self, tree: Tree) -> processor::Result {
let string = self
.formatter
.fmt(&tree)
.map_err(|e| processor::error(tree, e.into()))?;
print!("{string}");
Ok(())
}
}