use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use crate::update::Update;
#[derive(Debug)]
pub struct DispatchError {
message: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
}
impl DispatchError {
pub fn msg(message: impl Into<String>) -> Self {
Self {
message: message.into(),
source: None,
}
}
pub fn wrap(source: impl std::error::Error + Send + Sync + 'static) -> Self {
Self {
message: source.to_string(),
source: Some(Box::new(source)),
}
}
}
impl fmt::Display for DispatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for DispatchError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source.as_ref().map(|e| e.as_ref() as _)
}
}
pub type DispatchResult = Result<(), DispatchError>;
pub type BoxFuture = Pin<Box<dyn Future<Output = DispatchResult> + Send + 'static>>;
#[derive(Clone)]
pub struct Next {
chain: Arc<[Arc<dyn Middleware>]>,
index: usize,
endpoint: Arc<dyn Fn(Update) -> BoxFuture + Send + Sync>,
}
impl Next {
pub(crate) fn new(
chain: Arc<[Arc<dyn Middleware>]>,
endpoint: Arc<dyn Fn(Update) -> BoxFuture + Send + Sync>,
) -> Self {
Self {
chain,
index: 0,
endpoint,
}
}
pub fn run(self, update: Update) -> BoxFuture {
Box::pin(async move {
if self.index < self.chain.len() {
let mw = Arc::clone(&self.chain[self.index]);
let next = Next {
chain: Arc::clone(&self.chain),
index: self.index + 1,
endpoint: Arc::clone(&self.endpoint),
};
mw.call(update, next).await
} else {
(self.endpoint)(update).await
}
})
}
}
impl fmt::Debug for Next {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Next")
.field("index", &self.index)
.field("chain_len", &self.chain.len())
.finish()
}
}
pub trait Middleware: Send + Sync + 'static {
fn call(&self, update: Update, next: Next) -> BoxFuture;
}
impl<F, Fut> Middleware for F
where
F: Fn(Update, Next) -> Fut + Send + Sync + 'static,
Fut: Future<Output = DispatchResult> + Send + 'static,
{
fn call(&self, update: Update, next: Next) -> BoxFuture {
Box::pin((self)(update, next))
}
}