use crate::printer::{MakeStderr, MakeStdout, Pretty, Printer};
use crate::tree::Tree;
use std::error;
use std::sync::Arc;
use thiserror::Error;
#[derive(Error, Debug)]
#[error("{source}")]
pub struct Error {
pub tree: Tree,
source: Box<dyn error::Error + Send + Sync>,
}
pub fn error(tree: Tree, source: Box<dyn error::Error + Send + Sync>) -> Error {
Error { tree, source }
}
pub type Result = std::result::Result<(), Error>;
pub trait Processor: 'static + Sized {
#[allow(clippy::result_large_err)]
fn process(&self, tree: Tree) -> Result;
fn or<P: Processor>(self, processor: P) -> WithFallback<Self, P> {
WithFallback {
primary: self,
fallback: processor,
}
}
fn or_stdout(self) -> WithFallback<Self, Printer<Pretty, MakeStdout>> {
self.or(Printer::new().writer(MakeStdout))
}
fn or_stderr(self) -> WithFallback<Self, Printer<Pretty, MakeStderr>> {
self.or(Printer::new().writer(MakeStderr))
}
fn or_none(self) -> WithFallback<Self, Sink> {
self.or(Sink)
}
}
#[derive(Debug)]
pub struct WithFallback<P, F> {
primary: P,
fallback: F,
}
#[derive(Debug)]
pub struct Sink;
#[derive(Debug)]
pub struct FromFn<F>(F);
pub fn from_fn<F>(f: F) -> FromFn<F>
where
F: 'static + Fn(Tree) -> Result,
{
FromFn(f)
}
impl<P, F> Processor for WithFallback<P, F>
where
P: Processor,
F: Processor,
{
fn process(&self, tree: Tree) -> Result {
self.primary.process(tree).or_else(|err| {
eprintln!("{err}, using fallback processor...");
self.fallback.process(err.tree)
})
}
}
impl Processor for Sink {
fn process(&self, _tree: Tree) -> Result {
Ok(())
}
}
impl<F> Processor for FromFn<F>
where
F: 'static + Fn(Tree) -> Result,
{
fn process(&self, tree: Tree) -> Result {
(self.0)(tree)
}
}
impl<P: Processor> Processor for Box<P> {
fn process(&self, tree: Tree) -> Result {
self.as_ref().process(tree)
}
}
impl<P: Processor> Processor for Arc<P> {
fn process(&self, tree: Tree) -> Result {
self.as_ref().process(tree)
}
}