use futures::future::BoxFuture;
use std::{convert::Infallible, fmt::Debug, future::Future, sync::Arc};
pub trait ErrorHandler<E> {
#[must_use]
fn handle_error(self: Arc<Self>, error: E) -> BoxFuture<'static, ()>;
}
impl<E, F, Fut> ErrorHandler<E> for F
where
F: Fn(E) -> Fut + Send + Sync + 'static,
E: Send + 'static,
Fut: Future<Output = ()> + Send,
{
fn handle_error(self: Arc<Self>, error: E) -> BoxFuture<'static, ()> {
Box::pin(async move { self(error).await })
}
}
pub trait OnError<E> {
#[must_use]
fn on_error<'a, Eh>(self, eh: Arc<Eh>) -> BoxFuture<'a, ()>
where
Self: 'a,
Eh: ErrorHandler<E> + Send + Sync,
Arc<Eh>: 'a;
#[must_use]
fn log_on_error<'a>(self) -> BoxFuture<'a, ()>
where
Self: Sized + 'a,
E: Debug,
{
self.on_error(LoggingErrorHandler::new())
}
}
impl<T, E> OnError<E> for Result<T, E>
where
T: Send,
E: Send,
{
fn on_error<'a, Eh>(self, eh: Arc<Eh>) -> BoxFuture<'a, ()>
where
Self: 'a,
Eh: ErrorHandler<E> + Send + Sync,
Arc<Eh>: 'a,
{
Box::pin(async move {
if let Err(error) = self {
eh.handle_error(error).await;
}
})
}
}
#[derive(Clone, Copy)]
pub struct IgnoringErrorHandler;
impl IgnoringErrorHandler {
#[must_use]
pub fn new() -> Arc<Self> {
Arc::new(Self)
}
}
impl<E> ErrorHandler<E> for IgnoringErrorHandler {
fn handle_error(self: Arc<Self>, _: E) -> BoxFuture<'static, ()> {
Box::pin(async {})
}
}
#[derive(Clone, Copy)]
pub struct IgnoringErrorHandlerSafe;
impl IgnoringErrorHandlerSafe {
#[must_use]
pub fn new() -> Arc<Self> {
Arc::new(Self)
}
}
#[allow(unreachable_code)]
impl ErrorHandler<Infallible> for IgnoringErrorHandlerSafe {
fn handle_error(self: Arc<Self>, _: Infallible) -> BoxFuture<'static, ()> {
Box::pin(async {})
}
}
pub struct LoggingErrorHandler {
text: String,
}
impl LoggingErrorHandler {
#[must_use]
pub fn with_custom_text<T>(text: T) -> Arc<Self>
where
T: Into<String>,
{
Arc::new(Self { text: text.into() })
}
#[must_use]
pub fn new() -> Arc<Self> {
Self::with_custom_text("Error".to_owned())
}
}
impl<E> ErrorHandler<E> for LoggingErrorHandler
where
E: Debug,
{
fn handle_error(self: Arc<Self>, error: E) -> BoxFuture<'static, ()> {
log::error!("{text}: {:?}", error, text = self.text);
Box::pin(async {})
}
}