Skip to main content

ntex_service/
chain.rs

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