use std::{fmt, marker::PhantomData, rc::Rc};
use crate::dev::{Apply, ApplyCtx};
use crate::{IntoServiceFactory, Service, ServiceChainFactory, ServiceFactory};
pub fn apply<Sf, St, Req, M>(
mw: M,
factory: impl IntoServiceFactory<Sf, St, Req>,
) -> ServiceChainFactory<ApplyMiddleware<M, Sf>, St, Req>
where
Sf: ServiceFactory<St, Req>,
M: Middleware<Sf::Service, St>,
{
ServiceChainFactory {
factory: ApplyMiddleware::new(mw, factory.into_factory()),
_t: PhantomData,
}
}
pub trait Middleware<S, St> {
type Service;
fn create(&self, st: &St, service: S) -> Self::Service;
fn apply_to<Sf, Req>(
self,
factory: Sf,
) -> ServiceChainFactory<ApplyMiddleware<Self, Sf>, St, Req>
where
Sf: ServiceFactory<St, Req, Service = S>,
Self: Sized,
Self::Service: Service<St, Req>,
{
crate::factory(ApplyMiddleware::new(self, factory))
}
}
impl<M, S, St> Middleware<S, St> for Rc<M>
where
M: Middleware<S, St>,
{
type Service = M::Service;
fn create(&self, st: &St, service: S) -> M::Service {
self.as_ref().create(st, service)
}
}
pub struct ApplyMiddleware<M, Sf>(Rc<(M, Sf)>);
impl<M, Sf> ApplyMiddleware<M, Sf> {
pub(crate) fn new(mw: M, sf: Sf) -> Self {
Self(Rc::new((mw, sf)))
}
}
impl<M, Sf> Clone for ApplyMiddleware<M, Sf> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<M, Sf> fmt::Debug for ApplyMiddleware<M, Sf>
where
M: fmt::Debug,
Sf: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ApplyMiddleware")
.field("factory", &self.0.1)
.field("middleware", &self.0.0)
.finish()
}
}
impl<M, Sf, St, Req> ServiceFactory<St, Req> for ApplyMiddleware<M, Sf>
where
Sf: ServiceFactory<St, Req>,
M: Middleware<Sf::Service, St>,
M::Service: Service<St, Req>,
{
type Res = <M::Service as Service<St, Req>>::Res;
type Error = <M::Service as Service<St, Req>>::Error;
type Service = M::Service;
type InitError = Sf::InitError;
#[inline]
async fn create(&self, st: &St) -> Result<Self::Service, Self::InitError> {
Ok(self.0.0.create(st, self.0.1.create(st).await?))
}
}
#[derive(Debug, Clone, Copy)]
pub struct Identity;
impl<S, St> Middleware<S, St> for Identity {
type Service = S;
#[inline]
fn create(&self, _: &St, service: S) -> Self::Service {
service
}
}
#[derive(Debug, Clone)]
pub struct Stack<Inner, Outer> {
inner: Inner,
outer: Outer,
}
impl<Inner, Outer> Stack<Inner, Outer> {
pub fn new(inner: Inner, outer: Outer) -> Self {
Stack { inner, outer }
}
}
impl<S, St, Inner, Outer> Middleware<S, St> for Stack<Inner, Outer>
where
Inner: Middleware<S, St>,
Outer: Middleware<Inner::Service, St>,
{
type Service = Outer::Service;
fn create(&self, st: &St, service: S) -> Self::Service {
self.outer.create(st, self.inner.create(st, service))
}
}
#[doc(hidden)]
pub fn fn_layer<F, S, St, Req, In, Out, Err>(f: F) -> FnMiddleware<F, S, St, Req, In, Out, Err>
where
F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err> + Clone,
S: Service<St, Req>,
{
FnMiddleware { f, r: PhantomData }
}
pub struct FnMiddleware<F, S, St, Req, In, Out, Err> {
f: F,
r: PhantomData<fn(S, St, Req) -> (In, Out, Err)>,
}
impl<F, S, St, Req, In, Out, Err> Clone for FnMiddleware<F, S, St, Req, In, Out, Err>
where
F: Clone,
{
fn clone(&self) -> Self {
FnMiddleware {
f: self.f.clone(),
r: PhantomData,
}
}
}
impl<F, S, St, Req, In, Out, Err> fmt::Debug for FnMiddleware<F, S, St, Req, In, Out, Err> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FnMiddleware")
.field("layer", &std::any::type_name::<F>())
.finish()
}
}
impl<F, S, St, Req, In, Out, Err> Middleware<S, St> for FnMiddleware<F, S, St, Req, In, Out, Err>
where
S: Service<St, Req>,
F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err> + Clone,
Err: From<S::Error>,
{
type Service = Apply<S, St, Req, F, In, Out, Err>;
fn create(&self, _: &St, service: S) -> Self::Service {
Apply::new(service, self.f.clone())
}
}
#[cfg(test)]
#[allow(clippy::redundant_clone)]
#[path = "../tests/unit/middleware.rs"]
mod tests;