Skip to main content

ntex_service/
map_state.rs

1use crate::{Ctx, IntoService, IntoServiceFactory, Service, ServiceFactory};
2
3/// Wraps a service with a fixed state value.
4///
5/// The wrapped service uses `st` instead of the state from its outer pipeline.
6pub fn map_state<S, St, Req>(st: St, s: impl IntoService<S, St, Req>) -> MapState<S, St>
7where
8    S: Service<St, Req>,
9{
10    MapState {
11        st,
12        s: s.into_service(),
13    }
14}
15
16/// Wraps a service factory with a fixed state value.
17///
18/// The fixed state is used both to create services and to process their calls.
19pub fn map_state_factory<Sf, St, Req>(
20    st: St,
21    sf: impl IntoServiceFactory<Sf, St, Req>,
22) -> MapStateFactory<Sf, St>
23where
24    Sf: ServiceFactory<St, Req>,
25    St: Clone,
26{
27    MapStateFactory {
28        st,
29        sf: sf.into_factory(),
30    }
31}
32
33#[derive(Clone, Debug)]
34/// A service that substitutes fixed state for the outer pipeline state.
35pub struct MapState<S, St> {
36    s: S,
37    st: St,
38}
39
40impl<OtSt, S, St, Req> Service<OtSt, Req> for MapState<S, St>
41where
42    S: Service<St, Req>,
43{
44    type Res = S::Res;
45    type Error = S::Error;
46
47    #[inline]
48    async fn call(&self, req: Req, ctx: Ctx<'_, Self, OtSt>) -> Result<S::Res, S::Error> {
49        ctx.map_state(&self.st).call_nowait(&self.s, req).await
50    }
51
52    #[inline]
53    async fn ready(&self, ctx: Ctx<'_, Self, OtSt>) -> Result<(), S::Error> {
54        ctx.map_state(&self.st).ready(&self.s).await
55    }
56
57    #[inline]
58    async fn shutdown(&self, ctx: Ctx<'_, Self, OtSt>) {
59        ctx.map_state(&self.st).shutdown(&self.s).await;
60    }
61}
62
63#[derive(Clone, Debug)]
64/// A factory that creates [`MapState`] services using fixed state.
65pub struct MapStateFactory<Sf, St> {
66    sf: Sf,
67    st: St,
68}
69
70impl<OtSt, Sf, St, Req> ServiceFactory<OtSt, Req> for MapStateFactory<Sf, St>
71where
72    Sf: ServiceFactory<St, Req>,
73    St: Clone,
74{
75    type Res = Sf::Res;
76    type Error = Sf::Error;
77
78    type Service = MapState<Sf::Service, St>;
79    type InitError = Sf::InitError;
80
81    #[inline]
82    async fn create(&self, _: &OtSt) -> Result<Self::Service, Self::InitError> {
83        Ok(MapState {
84            s: self.sf.create(&self.st).await?,
85            st: self.st.clone(),
86        })
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use crate::{Pipeline, ServiceFactory, fn_service_st, map_state, map_state_factory};
93
94    #[ntex::test]
95    async fn test_map_state() {
96        let svc = map_state(
97            100,
98            fn_service_st(|_: &usize, item: usize| async move { Ok::<_, ()>(item) }),
99        )
100        .clone();
101        let _ = format!("{svc:?}");
102
103        let svc = Pipeline::new((), svc);
104        assert_eq!(svc.call(1).await.unwrap(), 1);
105        assert!(!svc.is_shutdown());
106        svc.shutdown().await;
107        assert!(svc.is_shutdown());
108
109        let factory = map_state_factory(
110            100,
111            fn_service_st(|_: &usize, item: usize| async move { Ok::<_, ()>(item) }),
112        )
113        .clone();
114        let _ = format!("{factory:?}");
115
116        let svc = Pipeline::new((), factory.create(&1).await.unwrap());
117        assert_eq!(svc.call(1).await.unwrap(), 1);
118    }
119}