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/// Starts a [`ServiceChain`] 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/// Starts a [`ServiceChainFactory`] 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/// A builder for composing services and combinators into one service.
41pub struct ServiceChain<S, St, Req> {
42    service: S,
43    st: PhantomData<(St, Req)>,
44}
45
46/// A builder for composing service factories and combinators.
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    /// Calls another service after this service completes successfully.
54    ///
55    /// The current service's response becomes the next service's request. If
56    /// the current service returns an error, the next service is not called.
57    pub fn and_then<Next, F>(self, service: F) -> ServiceChain<AndThen<S, Next>, St, Req>
58    where
59        Self: Sized,
60        F: IntoService<Next, St, S::Res>,
61        Next: Service<St, S::Res>,
62    {
63        ServiceChain {
64            service: AndThen::new(self.service, service.into_service()),
65            st: PhantomData,
66        }
67    }
68
69    /// Calls another service after this service completes.
70    ///
71    /// The next service receives the current service's complete `Result`, so it
72    /// can handle either a response or an error.
73    pub fn then<Next, F>(self, service: F) -> ServiceChain<Then<S, Next>, St, Req>
74    where
75        Self: Sized,
76        F: IntoService<Next, St, Result<S::Res, S::Error>>,
77        Next: Service<St, Result<S::Res, S::Error>>,
78    {
79        ServiceChain {
80            service: Then::new(self.service, service.into_service()),
81            st: PhantomData,
82        }
83    }
84
85    /// Maps this service's response to a different type.
86    ///
87    /// This is analogous to [`Option::map`] or [`Result::map`].
88    pub fn map<F, Res>(self, f: F) -> ServiceChain<Map<F, S, Res>, St, Req>
89    where
90        Self: Sized,
91        F: Fn(S::Res) -> Res,
92    {
93        ServiceChain {
94            service: Map::new(f, self.service),
95            st: PhantomData,
96        }
97    }
98
99    /// Maps this service's error to a different type.
100    ///
101    /// This is analogous to [`Result::map_err`] and is useful for normalizing
102    /// error types across composed services.
103    pub fn map_err<F, Err>(self, f: F) -> ServiceChain<MapErr<F, S, Err>, St, Req>
104    where
105        Self: Sized,
106        F: Fn(S::Error) -> Err,
107    {
108        ServiceChain {
109            service: MapErr::new(f, self.service),
110            st: PhantomData,
111        }
112    }
113
114    /// Adds a custom readiness check to the service chain.
115    ///
116    /// The check runs after the wrapped service's own readiness check succeeds;
117    /// the chain is ready only when both are ready.
118    pub fn readiness<F>(
119        self,
120        ready: F,
121    ) -> ServiceChain<AndThen<S, FnReadiness<F, S::Error>>, St, Req>
122    where
123        Self: Sized,
124        F: AsyncFn(&St) -> Result<(), S::Error>,
125    {
126        ServiceChain {
127            service: AndThen::new(self.service, FnReadiness::new(ready)),
128            st: PhantomData,
129        }
130    }
131
132    /// Adds a callback that runs once when the service shuts down.
133    pub fn shutdown<F>(self, sh: F) -> ServiceChain<AndThen<S, FnShutdown<F, S::Error>>, St, Req>
134    where
135        Self: Sized,
136        F: AsyncFnOnce(&St),
137    {
138        ServiceChain {
139            service: AndThen::new(self.service, FnShutdown::new(sh)),
140            st: PhantomData,
141        }
142    }
143
144    /// Applies an asynchronous function as middleware to this service.
145    ///
146    /// This is shorthand for calling [`crate::apply_fn`] on the chained service.
147    pub fn apply_fn<F, In, Out, Err>(
148        self,
149        f: F,
150    ) -> ServiceChain<Apply<S, St, Req, F, In, Out, Err>, St, In>
151    where
152        F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err>,
153        Err: From<S::Error>,
154    {
155        crate::apply_fn(self.service, f)
156    }
157}
158
159impl<S: Service<St, Req>, St, Req> Clone for ServiceChain<S, St, Req>
160where
161    S: Clone,
162{
163    fn clone(&self) -> Self {
164        ServiceChain {
165            service: self.service.clone(),
166            st: PhantomData,
167        }
168    }
169}
170
171impl<S: Service<St, Req>, St, Req> fmt::Debug for ServiceChain<S, St, Req>
172where
173    S: fmt::Debug,
174{
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        f.debug_struct("ServiceChain")
177            .field("service", &self.service)
178            .finish()
179    }
180}
181
182impl<S: Service<St, Req>, St, Req> Service<St, Req> for ServiceChain<S, St, Req> {
183    type Res = S::Res;
184    type Error = S::Error;
185
186    #[inline]
187    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error> {
188        ctx.call(&self.service, req).await
189    }
190
191    crate::forward_ready!(St, service);
192    crate::forward_shutdown!(St, service);
193}
194
195impl<Sf: ServiceFactory<St, Req>, St, Req> ServiceChainFactory<Sf, St, Req> {
196    /// Chains another factory after this factory's services.
197    ///
198    /// Each created service passes its response to the next service as its request.
199    /// If the first service returns an error, the next service is not called.
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    /// Applies middleware to this service factory.
215    ///
216    /// This is shorthand for calling [`crate::apply`] on the chained 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    /// Applies an asynchronous function as middleware to this service factory.
225    ///
226    /// This is shorthand for calling [`crate::apply_fn_factory`] on the chained
227    /// factory.
228    pub fn apply_fn<F, In, Out, Err>(
229        self,
230        f: F,
231    ) -> ServiceChainFactory<ApplyFactory<F, Sf, St, Req, In, Out, Err>, St, In>
232    where
233        F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
234        Err: From<Sf::Error>,
235    {
236        crate::apply_fn_factory(self.factory, f)
237    }
238
239    /// Chains a factory whose services receive the preceding service's complete
240    /// `Result`.
241    pub fn then<F, U>(self, factory: F) -> ServiceChainFactory<ThenFactory<Sf, U>, St, Req>
242    where
243        Self: Sized,
244        F: IntoServiceFactory<U, St, Result<Sf::Res, Sf::Error>>,
245        U: ServiceFactory<
246                St,
247                Result<Sf::Res, Sf::Error>,
248                Error = Sf::Error,
249                InitError = Sf::InitError,
250            >,
251    {
252        ServiceChainFactory {
253            factory: ThenFactory::new(self.factory, factory.into_factory()),
254            _t: PhantomData,
255        }
256    }
257
258    /// Maps responses produced by this factory's services.
259    pub fn map<F, Res>(self, f: F) -> ServiceChainFactory<MapFactory<F, Sf, Res>, St, Req>
260    where
261        Self: Sized,
262        F: Fn(Sf::Res) -> Res + Clone,
263    {
264        ServiceChainFactory {
265            factory: MapFactory::new(f, self.factory),
266            _t: PhantomData,
267        }
268    }
269
270    /// Maps errors produced by this factory's services.
271    pub fn map_err<F, E>(self, f: F) -> ServiceChainFactory<MapErrFactory<F, Sf, E>, St, Req>
272    where
273        Self: Sized,
274        F: Fn(Sf::Error) -> E + Clone,
275    {
276        ServiceChainFactory {
277            factory: MapErrFactory::new(f, self.factory),
278            _t: PhantomData,
279        }
280    }
281
282    /// Maps this factory's initialization error.
283    pub fn map_init_err<F, E>(self, f: F) -> ServiceChainFactory<MapInitErr<F, Sf, E>, St, Req>
284    where
285        Self: Sized,
286        F: Fn(Sf::InitError) -> E + Clone,
287    {
288        ServiceChainFactory {
289            factory: MapInitErr::new(f, self.factory),
290            _t: PhantomData,
291        }
292    }
293
294    /// Adds a custom readiness check to each created service.
295    ///
296    /// The check runs after the wrapped service's own readiness check succeeds;
297    /// the service is ready only when both are ready.
298    pub fn readiness<F>(
299        self,
300        ready: F,
301    ) -> ServiceChainFactory<AndThenFactory<Sf, FnReadiness<F, Sf::Error>>, St, Req>
302    where
303        Self: Sized,
304        F: AsyncFn(&St) -> Result<(), Sf::Error> + Clone,
305    {
306        ServiceChainFactory {
307            factory: AndThenFactory::new(self.factory, FnReadiness::new(ready)),
308            _t: PhantomData,
309        }
310    }
311
312    /// Adds a shutdown callback to each created service.
313    pub fn shutdown<F>(
314        self,
315        sh: F,
316    ) -> ServiceChainFactory<AndThenFactory<Sf, FnShutdown<F, Sf::Error>>, St, Req>
317    where
318        Self: Sized,
319        F: AsyncFnOnce(&St) + Clone,
320    {
321        ServiceChainFactory {
322            factory: AndThenFactory::new(self.factory, FnShutdown::new(sh)),
323            _t: PhantomData,
324        }
325    }
326
327    /// Creates a service and wraps it with its state in a [`Pipeline`].
328    pub async fn pipeline(&self, st: St) -> Result<Pipeline<Req, Sf::Res, Sf::Error>, Sf::InitError>
329    where
330        Sf: 'static,
331        St: 'static,
332        Req: 'static,
333    {
334        let svc = self.factory.create(&st).await?;
335        Ok(Pipeline::new(st, svc))
336    }
337}
338
339impl<Sf, St, Req> Clone for ServiceChainFactory<Sf, St, Req>
340where
341    Sf: Clone,
342{
343    fn clone(&self) -> Self {
344        ServiceChainFactory {
345            factory: self.factory.clone(),
346            _t: PhantomData,
347        }
348    }
349}
350
351impl<Sf, St, Req> fmt::Debug for ServiceChainFactory<Sf, St, Req>
352where
353    Sf: fmt::Debug,
354{
355    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356        f.debug_struct("ServiceChainFactory")
357            .field("factory", &self.factory)
358            .finish()
359    }
360}
361
362impl<Sf: ServiceFactory<St, Req>, St, Req> ServiceFactory<St, Req>
363    for ServiceChainFactory<Sf, St, Req>
364{
365    type Res = Sf::Res;
366    type Error = Sf::Error;
367
368    type Service = Sf::Service;
369    type InitError = Sf::InitError;
370
371    #[inline]
372    async fn create(&self, st: &St) -> Result<Sf::Service, Sf::InitError> {
373        self.factory.create(st).await
374    }
375}