Skip to main content

ntex_service/
chain.rs

1use std::{fmt, marker::PhantomData};
2
3use crate::and_then::{AndThen, AndThenFactory};
4use crate::apply::{Apply, ApplyCtx, ApplyFactory};
5use crate::ctx::Ctx;
6use crate::fn_ready::FnReadiness;
7use crate::fn_shutdown::FnShutdown;
8use crate::map::{Map, MapFactory};
9use crate::map_err::{MapErr, MapErrFactory};
10use crate::map_init_err::MapInitErr;
11use crate::middleware::{ApplyMiddleware, Middleware};
12use crate::pipeline::Pipeline;
13use crate::then::{Then, ThenFactory};
14use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory};
15
16/// Constructs new chain with one service.
17pub fn service<S, St, Req>(service: impl IntoService<S, St, Req>) -> ServiceChain<S, St, Req>
18where
19    S: Service<St, Req>,
20{
21    ServiceChain {
22        service: service.into_service(),
23        st: PhantomData,
24    }
25}
26
27/// Constructs new chain factory with one service factory.
28pub fn factory<Sf, St, Req>(
29    factory: impl IntoServiceFactory<Sf, St, Req>,
30) -> ServiceChainFactory<Sf, St, Req>
31where
32    Sf: ServiceFactory<St, Req>,
33{
34    ServiceChainFactory {
35        factory: factory.into_factory(),
36        _t: PhantomData,
37    }
38}
39
40/// Chain builder - chain allows to compose multiple service into one service.
41pub struct ServiceChain<S, St, Req> {
42    service: S,
43    st: PhantomData<(St, Req)>,
44}
45
46/// Service factory builder
47pub struct ServiceChainFactory<Sf, St, Req> {
48    pub(crate) factory: Sf,
49    pub(crate) _t: PhantomData<(St, Req)>,
50}
51
52impl<S: Service<St, Req>, St, Req> ServiceChain<S, St, Req> {
53    /// Call another service after call to this one has resolved successfully.
54    ///
55    /// This function can be used to chain two services together and ensure that
56    /// the second service isn't called until call to the fist service have
57    /// finished. Result of the call to the first service is used as an
58    /// input parameter for the second service's call.
59    ///
60    /// Note that this function consumes the receiving service and returns a
61    /// wrapped version of it.
62    pub fn and_then<Next, F>(self, service: F) -> ServiceChain<AndThen<S, Next>, St, Req>
63    where
64        Self: Sized,
65        F: IntoService<Next, St, S::Res>,
66        Next: Service<St, S::Res>,
67    {
68        ServiceChain {
69            service: AndThen::new(self.service, service.into_service()),
70            st: PhantomData,
71        }
72    }
73
74    /// Chain on a computation for when a call to the service finished,
75    /// passing the result of the call to the next service `U`.
76    pub fn then<Next, F>(self, service: F) -> ServiceChain<Then<S, Next>, St, Req>
77    where
78        Self: Sized,
79        F: IntoService<Next, St, Result<S::Res, S::Error>>,
80        Next: Service<St, Result<S::Res, S::Error>>,
81    {
82        ServiceChain {
83            service: Then::new(self.service, service.into_service()),
84            st: PhantomData,
85        }
86    }
87
88    /// Map this service's output to a different type, returning a new service
89    /// of the resulting type.
90    ///
91    /// This function is similar to the `Option::map` or `Iterator::map` where
92    /// it will change the type of the underlying service.
93    pub fn map<F, Res>(self, f: F) -> ServiceChain<Map<F, S, Res>, St, Req>
94    where
95        Self: Sized,
96        F: Fn(S::Res) -> Res,
97    {
98        ServiceChain {
99            service: Map::new(f, self.service),
100            st: PhantomData,
101        }
102    }
103
104    /// Map this service's error to a different error, returning a new service.
105    ///
106    /// This function is similar to the `Result::map_err` where it will change
107    /// the error type of the underlying service. This is useful for example to
108    /// ensure that services have the same error type.
109    pub fn map_err<F, Err>(self, f: F) -> ServiceChain<MapErr<F, S, Err>, St, Req>
110    where
111        Self: Sized,
112        F: Fn(S::Error) -> Err,
113    {
114        ServiceChain {
115            service: MapErr::new(f, self.service),
116            st: PhantomData,
117        }
118    }
119
120    /// Add custom readiness check to the service chain.
121    pub fn readiness<F>(
122        self,
123        ready: F,
124    ) -> ServiceChain<AndThen<S, FnReadiness<F, S::Error>>, St, Req>
125    where
126        Self: Sized,
127        F: AsyncFn(&St) -> Result<(), S::Error>,
128    {
129        ServiceChain {
130            service: AndThen::new(self.service, FnReadiness::new(ready)),
131            st: PhantomData,
132        }
133    }
134
135    /// Add custom readiness check to the service chain.
136    pub fn shutdown<F>(self, sh: F) -> ServiceChain<AndThen<S, FnShutdown<F, S::Error>>, St, Req>
137    where
138        Self: Sized,
139        F: AsyncFnOnce(&St),
140    {
141        ServiceChain {
142            service: AndThen::new(self.service, FnShutdown::new(sh)),
143            st: PhantomData,
144        }
145    }
146
147    /// Use function as middleware for current service.
148    ///
149    /// Short version of `apply_fn(service(...), fn)`
150    pub fn apply_fn<F, In, Out, Err>(
151        self,
152        f: F,
153    ) -> ServiceChain<Apply<S, St, Req, F, In, Out, Err>, St, In>
154    where
155        F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err>,
156        Err: From<S::Error>,
157    {
158        crate::apply_fn(self.service, f)
159    }
160}
161
162impl<S: Service<St, Req>, St, Req> Clone for ServiceChain<S, St, Req>
163where
164    S: Clone,
165{
166    fn clone(&self) -> Self {
167        ServiceChain {
168            service: self.service.clone(),
169            st: PhantomData,
170        }
171    }
172}
173
174impl<S: Service<St, Req>, St, Req> fmt::Debug for ServiceChain<S, St, Req>
175where
176    S: fmt::Debug,
177{
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        f.debug_struct("ServiceChain")
180            .field("service", &self.service)
181            .finish()
182    }
183}
184
185impl<S: Service<St, Req>, St, Req> Service<St, Req> for ServiceChain<S, St, Req> {
186    type Res = S::Res;
187    type Error = S::Error;
188
189    #[inline]
190    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error> {
191        ctx.call(&self.service, req).await
192    }
193
194    crate::forward_ready!(St, service);
195    crate::forward_shutdown!(St, service);
196}
197
198impl<Sf: ServiceFactory<St, Req>, St, Req> ServiceChainFactory<Sf, St, Req> {
199    /// Call another service after call to this one has resolved successfully.
200    pub fn and_then<U>(
201        self,
202        factory: impl IntoServiceFactory<U, St, Sf::Res>,
203    ) -> ServiceChainFactory<AndThenFactory<Sf, U>, St, Req>
204    where
205        Self: Sized,
206        U: ServiceFactory<St, Sf::Res, Error = Sf::Error, InitError = Sf::InitError>,
207    {
208        ServiceChainFactory {
209            factory: AndThenFactory::new(self.factory, factory.into_factory()),
210            _t: PhantomData,
211        }
212    }
213
214    /// Apply Middleware to current service factory.
215    ///
216    /// Short version of `apply(middleware, factory(...))`
217    pub fn apply<U>(self, tr: U) -> ServiceChainFactory<ApplyMiddleware<U, Sf>, St, Req>
218    where
219        U: Middleware<Sf::Service, St>,
220    {
221        crate::apply(tr, self.factory)
222    }
223
224    /// Apply function middleware to current service factory.
225    ///
226    /// Short version of `apply_fn_factory(factory(...), fn)`
227    pub fn apply_fn<F, In, Out, Err>(
228        self,
229        f: F,
230    ) -> ServiceChainFactory<ApplyFactory<F, Sf, St, Req, In, Out, Err>, St, In>
231    where
232        F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
233        Err: From<Sf::Error>,
234    {
235        crate::apply_fn_factory(self.factory, f)
236    }
237
238    /// Create chain factory to chain on a computation for when a call to the
239    /// service finished, passing the result of the call to the next
240    /// service `U`.
241    ///
242    /// Note that this function consumes the receiving factory and returns a
243    /// wrapped version of it.
244    pub fn then<F, U>(self, factory: F) -> ServiceChainFactory<ThenFactory<Sf, U>, St, Req>
245    where
246        Self: Sized,
247        F: IntoServiceFactory<U, St, Result<Sf::Res, Sf::Error>>,
248        U: ServiceFactory<
249                St,
250                Result<Sf::Res, Sf::Error>,
251                Error = Sf::Error,
252                InitError = Sf::InitError,
253            >,
254    {
255        ServiceChainFactory {
256            factory: ThenFactory::new(self.factory, factory.into_factory()),
257            _t: PhantomData,
258        }
259    }
260
261    /// Map this service's output to a different type, returning a new service
262    /// of the resulting type.
263    pub fn map<F, Res>(self, f: F) -> ServiceChainFactory<MapFactory<F, Sf, Res>, St, Req>
264    where
265        Self: Sized,
266        F: Fn(Sf::Res) -> Res + Clone,
267    {
268        ServiceChainFactory {
269            factory: MapFactory::new(f, self.factory),
270            _t: PhantomData,
271        }
272    }
273
274    /// Map this service's error to a different error.
275    pub fn map_err<F, E>(self, f: F) -> ServiceChainFactory<MapErrFactory<F, Sf, E>, St, Req>
276    where
277        Self: Sized,
278        F: Fn(Sf::Error) -> E + Clone,
279    {
280        ServiceChainFactory {
281            factory: MapErrFactory::new(f, self.factory),
282            _t: PhantomData,
283        }
284    }
285
286    /// Map this factory's init error to a different error, returning a new factory.
287    pub fn map_init_err<F, E>(self, f: F) -> ServiceChainFactory<MapInitErr<F, Sf, E>, St, Req>
288    where
289        Self: Sized,
290        F: Fn(Sf::InitError) -> E + Clone,
291    {
292        ServiceChainFactory {
293            factory: MapInitErr::new(f, self.factory),
294            _t: PhantomData,
295        }
296    }
297
298    /// Add custom readiness check to the service factory.
299    pub fn readiness<F>(
300        self,
301        ready: F,
302    ) -> ServiceChainFactory<AndThenFactory<Sf, FnReadiness<F, Sf::Error>>, St, Req>
303    where
304        Self: Sized,
305        F: AsyncFn(&St) -> Result<(), Sf::Error> + Clone,
306    {
307        ServiceChainFactory {
308            factory: AndThenFactory::new(self.factory, FnReadiness::new(ready)),
309            _t: PhantomData,
310        }
311    }
312
313    /// Add custom shutdown callback to the service factory.
314    pub fn shutdown<F>(
315        self,
316        sh: F,
317    ) -> ServiceChainFactory<AndThenFactory<Sf, FnShutdown<F, Sf::Error>>, St, Req>
318    where
319        Self: Sized,
320        F: AsyncFnOnce(&St) + Clone,
321    {
322        ServiceChainFactory {
323            factory: AndThenFactory::new(self.factory, FnShutdown::new(sh)),
324            _t: PhantomData,
325        }
326    }
327
328    /// Create and return a new service value asynchronously and wrap into a container
329    pub async fn pipeline(&self, st: St) -> Result<Pipeline<Req, Sf::Res, Sf::Error>, Sf::InitError>
330    where
331        Sf: 'static,
332        St: 'static,
333        Req: 'static,
334    {
335        let svc = self.factory.create(&st).await?;
336        Ok(Pipeline::new(st, svc))
337    }
338}
339
340impl<Sf, St, Req> Clone for ServiceChainFactory<Sf, St, Req>
341where
342    Sf: Clone,
343{
344    fn clone(&self) -> Self {
345        ServiceChainFactory {
346            factory: self.factory.clone(),
347            _t: PhantomData,
348        }
349    }
350}
351
352impl<Sf, St, Req> fmt::Debug for ServiceChainFactory<Sf, St, Req>
353where
354    Sf: fmt::Debug,
355{
356    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357        f.debug_struct("ServiceChainFactory")
358            .field("factory", &self.factory)
359            .finish()
360    }
361}
362
363impl<Sf: ServiceFactory<St, Req>, St, Req> ServiceFactory<St, Req>
364    for ServiceChainFactory<Sf, St, Req>
365{
366    type Res = Sf::Res;
367    type Error = Sf::Error;
368
369    type Service = Sf::Service;
370    type InitError = Sf::InitError;
371
372    #[inline]
373    async fn create(&self, st: &St) -> Result<Sf::Service, Sf::InitError> {
374        self.factory.create(st).await
375    }
376}