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