ntex-service 5.7.0

ntex service
Documentation
use crate::{Ctx, IntoService, IntoServiceFactory, Service, ServiceFactory};

/// Wraps a service with a fixed state value.
///
/// The wrapped service uses `st` instead of the state from its outer pipeline.
/// This changes the state passed to operations, not the service's readiness
/// identity. A shared binding keeps one gate across mapped state values.
pub fn map_state<S, St, Req>(st: St, s: impl IntoService<S, St, Req>) -> MapState<S, St>
where
    S: Service<St, Req>,
{
    MapState {
        st,
        s: s.into_service(),
    }
}

/// Wraps a service factory with a fixed state value.
///
/// The fixed state is used both to create services and to process their calls.
pub fn map_state_factory<Sf, St, Req>(
    st: St,
    sf: impl IntoServiceFactory<Sf, St, Req>,
) -> MapStateFactory<Sf, St>
where
    Sf: ServiceFactory<St, Req>,
    St: Clone,
{
    MapStateFactory {
        st,
        sf: sf.into_factory(),
    }
}

#[derive(Clone, Debug)]
/// A service that substitutes fixed state for the outer pipeline state.
///
/// Readiness and lifecycle subscriptions retain the wrapped service's scope.
/// Cloning follows the wrapped service: a [`crate::ReadyBinding`] clone gets
/// fresh readiness, while an explicitly shared handle keeps its existing gate.
pub struct MapState<S, St> {
    s: S,
    st: St,
}

impl<OtSt, S, St, Req> Service<OtSt, Req> for MapState<S, St>
where
    S: Service<St, Req>,
{
    type Res = S::Res;
    type Error = S::Error;

    fn bind(&self, ctx: crate::BindCtx<'_>) {
        ctx.bind(&self.s);
    }

    #[inline]
    async fn call(&self, req: Req, ctx: Ctx<'_, Self, OtSt>) -> Result<S::Res, S::Error> {
        self.s.call(req, ctx.map_state(&self.st).cast()).await
    }

    #[inline]
    async fn ready(&self, ctx: Ctx<'_, Self, OtSt>) -> Result<(), S::Error> {
        self.s.ready(ctx.map_state(&self.st).cast()).await
    }

    #[inline]
    async fn shutdown(&self, ctx: Ctx<'_, Self, OtSt>) {
        self.s.shutdown(ctx.map_state(&self.st).cast()).await;
    }
}

#[derive(Clone, Debug)]
/// A factory that creates [`MapState`] services using fixed state.
pub struct MapStateFactory<Sf, St> {
    sf: Sf,
    st: St,
}

impl<OtSt, Sf, St, Req> ServiceFactory<OtSt, Req> for MapStateFactory<Sf, St>
where
    Sf: ServiceFactory<St, Req>,
    St: Clone,
{
    type Res = Sf::Res;
    type Error = Sf::Error;

    type Service = MapState<Sf::Service, St>;
    type InitError = Sf::InitError;

    #[inline]
    async fn create(&self, _: &OtSt) -> Result<Self::Service, Self::InitError> {
        Ok(MapState {
            s: self.sf.create(&self.st).await?,
            st: self.st.clone(),
        })
    }
}

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