ntex-service 5.3.1

ntex service
Documentation
use std::{fmt, marker};

use crate::Ctx;
use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory};
use crate::{ServiceCaller, ServiceChain, ServiceChainFactory};

/// Applies an asynchronous middleware function to a service.
///
/// The function receives an input request and an [`ApplyCtx`] that can call the
/// wrapped service.
pub fn apply_fn<S, St, Req, F, In, Out, Err>(
    service: impl IntoService<S, St, Req>,
    f: F,
) -> ServiceChain<Apply<S, St, Req, F, In, Out, Err>, St, In>
where
    S: Service<St, Req>,
    F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err>,
    Err: From<S::Error>,
{
    crate::service(Apply::new(service.into_service(), f))
}

/// Applies an asynchronous middleware function to every service from a factory.
pub fn apply_fn_factory<Sf, St, Req, F, In, Out, Err>(
    service: impl IntoServiceFactory<Sf, St, Req>,
    f: F,
) -> ServiceChainFactory<ApplyFactory<F, Sf, St, Req, In, Out, Err>, St, In>
where
    Sf: ServiceFactory<St, Req>,
    F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
    Err: From<Sf::Error>,
{
    crate::factory(ApplyFactory::new(service.into_factory(), f))
}

#[derive(Debug)]
/// Context passed to middleware functions created by [`apply_fn`].
///
/// Calls forward directly to the inner service. Wrap it with [`crate::ready`]
/// to check its independent readiness on each call, including after middleware
/// awaits or makes another call.
pub struct ApplyCtx<'a, S, St, Req> {
    ctx: Ctx<'a, S, St>,
    service: &'a S,
    r: marker::PhantomData<Req>,
}

impl<S: Service<St, Req>, St, Req> ApplyCtx<'_, S, St, Req> {
    /// Returns the pipeline state.
    #[inline]
    pub fn st(&self) -> &St {
        self.ctx.st()
    }

    /// Calls the inner service, preserving any explicit readiness wrapper.
    #[inline]
    pub async fn call(&self, req: Req) -> Result<S::Res, S::Error> {
        self.service
            .call(req, self.ctx.without_bypass().cast())
            .await
    }
}

impl<S: Service<St, Req>, St, Req> ServiceCaller<Req, S::Res, S::Error>
    for ApplyCtx<'_, S, St, Req>
{
    #[inline]
    async fn call_service(&self, req: Req) -> Result<S::Res, S::Error> {
        self.call(req).await
    }
}

/// Service produced by [`apply_fn`].
///
/// Shares the enclosing readiness scope with its child unless the child is
/// explicitly wrapped with [`crate::ready`].
pub struct Apply<S, St, Req, F, In, Out, Err> {
    svc: S,
    f: F,
    r: marker::PhantomData<fn(St, Req) -> (In, Out, Err)>,
}

impl<S, St, Req, F, In, Out, Err> Apply<S, St, Req, F, In, Out, Err>
where
    F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err>,
{
    pub(crate) fn new(svc: S, f: F) -> Self {
        Apply {
            f,
            svc,
            r: marker::PhantomData,
        }
    }
}

impl<S, St, Req, F, In, Out, Err> Clone for Apply<S, St, Req, F, In, Out, Err>
where
    S: Clone,
    F: Clone,
{
    fn clone(&self) -> Self {
        Apply {
            svc: self.svc.clone(),
            f: self.f.clone(),
            r: marker::PhantomData,
        }
    }
}

impl<S, St, Req, F, In, Out, Err> fmt::Debug for Apply<S, St, Req, F, In, Out, Err>
where
    S: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Apply")
            .field("svc", &self.svc)
            .field("map", &std::any::type_name::<F>())
            .finish()
    }
}

impl<S, St, Req, F, In, Out, Err> Service<St, In> for Apply<S, St, Req, F, In, Out, Err>
where
    S: Service<St, Req>,
    F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err>,
    Err: From<S::Error>,
{
    type Res = Out;
    type Error = Err;

    crate::forward_bind!(St, svc);

    #[inline]
    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Err> {
        ctx.ready(&self.svc).await.map_err(From::from)
    }

    #[inline]
    async fn call(&self, req: In, ctx: Ctx<'_, Self, St>) -> Result<Out, Err> {
        let ctx = ApplyCtx {
            ctx: ctx.cast(),
            service: &self.svc,
            r: marker::PhantomData,
        };
        (self.f)(req, &ctx).await
    }

    crate::forward_shutdown!(St, svc);
}

/// Service factory produced by [`apply_fn_factory`].
pub struct ApplyFactory<F, Sf, St, Req, In, Out, Err>
where
    F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
    Sf: ServiceFactory<St, Req>,
{
    f: F,
    sf: Sf,
    r: marker::PhantomData<fn(St, Req) -> (In, Out)>,
}

impl<F, Sf, St, Req, In, Out, Err> ApplyFactory<F, Sf, St, Req, In, Out, Err>
where
    F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
    Sf: ServiceFactory<St, Req>,
{
    /// Creates a new `ApplyFactory`.
    pub(crate) fn new(sf: Sf, f: F) -> Self
    where
        Sf: ServiceFactory<St, Req>,
        Err: From<Sf::Error>,
    {
        Self {
            f,
            sf,
            r: marker::PhantomData,
        }
    }
}

impl<F, Sf, St, Req, In, Out, Err> Clone for ApplyFactory<F, Sf, St, Req, In, Out, Err>
where
    F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
    Sf: ServiceFactory<St, Req> + Clone,
{
    fn clone(&self) -> Self {
        Self {
            f: self.f.clone(),
            sf: self.sf.clone(),
            r: marker::PhantomData,
        }
    }
}

impl<F, Sf, St, Req, In, Out, Err> fmt::Debug for ApplyFactory<F, Sf, St, Req, In, Out, Err>
where
    F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
    Sf: ServiceFactory<St, Req> + fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ApplyFactory")
            .field("factory", &self.sf)
            .field("map", &std::any::type_name::<F>())
            .finish()
    }
}

impl<F, Sf, St, Req, In, Out, Err> ServiceFactory<St, In>
    for ApplyFactory<F, Sf, St, Req, In, Out, Err>
where
    F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
    Sf: ServiceFactory<St, Req>,
    Err: From<Sf::Error>,
{
    type Res = Out;
    type Error = Err;

    type Service = Apply<Sf::Service, St, Req, F, In, Out, Err>;
    type InitError = Sf::InitError;

    #[inline]
    async fn create(&self, st: &St) -> Result<Self::Service, Self::InitError> {
        self.sf
            .create(st)
            .await
            .map(|svc| Apply::new(svc, self.f.clone()))
    }
}

#[cfg(test)]
#[path = "../tests/unit/apply.rs"]
mod tests;