ntex_service/
chain.rs

1#![allow(clippy::type_complexity)]
2use std::{fmt, future::Future, 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<T, R, C, F>(factory: F) -> ServiceChainFactory<T, R, C>
28where
29    T: ServiceFactory<R, C>,
30    F: IntoServiceFactory<T, R, 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, R, In, Out, Err>(
126        self,
127        f: F,
128    ) -> ServiceChain<Apply<Svc, Req, F, R, In, Out, Err>, In>
129    where
130        F: Fn(In, Pipeline<Svc>) -> R,
131        R: Future<Output = Result<Out, Err>>,
132        Svc: Service<Req>,
133        Err: From<Svc::Error>,
134    {
135        ServiceChain {
136            service: Apply::new(self.service, f),
137            _t: PhantomData,
138        }
139    }
140
141    /// Create service pipeline
142    pub fn into_pipeline(self) -> Pipeline<Svc> {
143        Pipeline::new(self.service)
144    }
145}
146
147impl<Svc, Req> Clone for ServiceChain<Svc, Req>
148where
149    Svc: Clone,
150{
151    fn clone(&self) -> Self {
152        ServiceChain {
153            service: self.service.clone(),
154            _t: PhantomData,
155        }
156    }
157}
158
159impl<Svc, Req> fmt::Debug for ServiceChain<Svc, Req>
160where
161    Svc: fmt::Debug,
162{
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        f.debug_struct("ServiceChain")
165            .field("service", &self.service)
166            .finish()
167    }
168}
169
170impl<Svc: Service<Req>, Req> Service<Req> for ServiceChain<Svc, Req> {
171    type Response = Svc::Response;
172    type Error = Svc::Error;
173
174    crate::forward_poll!(service);
175    crate::forward_ready!(service);
176    crate::forward_shutdown!(service);
177
178    #[inline]
179    async fn call(
180        &self,
181        req: Req,
182        ctx: ServiceCtx<'_, Self>,
183    ) -> Result<Self::Response, Self::Error> {
184        ctx.call(&self.service, req).await
185    }
186}
187
188/// Service factory builder
189pub struct ServiceChainFactory<T, Req, C = ()> {
190    factory: T,
191    _t: PhantomData<(Req, C)>,
192}
193
194impl<T: ServiceFactory<Req, C>, Req, C> ServiceChainFactory<T, Req, C> {
195    /// Call another service after call to this one has resolved successfully.
196    pub fn and_then<F, U>(
197        self,
198        factory: F,
199    ) -> ServiceChainFactory<AndThenFactory<T, U>, Req, C>
200    where
201        Self: Sized,
202        F: IntoServiceFactory<U, T::Response, C>,
203        U: ServiceFactory<T::Response, C, Error = T::Error, InitError = T::InitError>,
204    {
205        ServiceChainFactory {
206            factory: AndThenFactory::new(self.factory, factory.into_factory()),
207            _t: PhantomData,
208        }
209    }
210
211    /// Apply middleware to current service factory.
212    ///
213    /// Short version of `apply(middleware, chain_factory(...))`
214    pub fn apply<U>(self, tr: U) -> ServiceChainFactory<ApplyMiddleware<U, T, C>, Req, C>
215    where
216        U: Middleware<T::Service>,
217    {
218        ServiceChainFactory {
219            factory: ApplyMiddleware::new(tr, self.factory),
220            _t: PhantomData,
221        }
222    }
223
224    /// Apply function middleware to current service factory.
225    ///
226    /// Short version of `apply_fn_factory(chain_factory(...), fn)`
227    pub fn apply_fn<F, R, In, Out, Err>(
228        self,
229        f: F,
230    ) -> ServiceChainFactory<ApplyFactory<T, Req, C, F, R, In, Out, Err>, In, C>
231    where
232        F: Fn(In, Pipeline<T::Service>) -> R + Clone,
233        R: Future<Output = Result<Out, Err>>,
234        T: ServiceFactory<Req, C>,
235        Err: From<T::Error>,
236    {
237        ServiceChainFactory {
238            factory: ApplyFactory::new(self.factory, f),
239            _t: PhantomData,
240        }
241    }
242
243    /// Create chain factory to chain on a computation for when a call to the
244    /// service finished, passing the result of the call to the next
245    /// service `U`.
246    ///
247    /// Note that this function consumes the receiving factory and returns a
248    /// wrapped version of it.
249    pub fn then<F, U>(self, factory: F) -> ServiceChainFactory<ThenFactory<T, U>, Req, C>
250    where
251        Self: Sized,
252        C: Clone,
253        F: IntoServiceFactory<U, Result<T::Response, T::Error>, C>,
254        U: ServiceFactory<
255            Result<T::Response, T::Error>,
256            C,
257            Error = T::Error,
258            InitError = T::InitError,
259        >,
260    {
261        ServiceChainFactory {
262            factory: ThenFactory::new(self.factory, factory.into_factory()),
263            _t: PhantomData,
264        }
265    }
266
267    /// Map this service's output to a different type, returning a new service
268    /// of the resulting type.
269    pub fn map<F, Res>(
270        self,
271        f: F,
272    ) -> ServiceChainFactory<MapFactory<T, F, Req, Res, C>, Req, C>
273    where
274        Self: Sized,
275        F: Fn(T::Response) -> Res + Clone,
276    {
277        ServiceChainFactory {
278            factory: MapFactory::new(self.factory, f),
279            _t: PhantomData,
280        }
281    }
282
283    /// Map this service's error to a different error.
284    pub fn map_err<F, E>(
285        self,
286        f: F,
287    ) -> ServiceChainFactory<MapErrFactory<T, Req, C, F, E>, Req, C>
288    where
289        Self: Sized,
290        F: Fn(T::Error) -> E + Clone,
291    {
292        ServiceChainFactory {
293            factory: MapErrFactory::new(self.factory, f),
294            _t: PhantomData,
295        }
296    }
297
298    /// Map this factory's init error to a different error, returning a new factory.
299    pub fn map_init_err<F, E>(
300        self,
301        f: F,
302    ) -> ServiceChainFactory<MapInitErr<T, Req, C, F, E>, Req, C>
303    where
304        Self: Sized,
305        F: Fn(T::InitError) -> E + Clone,
306    {
307        ServiceChainFactory {
308            factory: MapInitErr::new(self.factory, f),
309            _t: PhantomData,
310        }
311    }
312
313    /// Create and return a new service value asynchronously and wrap into a container
314    pub async fn pipeline(&self, cfg: C) -> Result<Pipeline<T::Service>, T::InitError>
315    where
316        Self: Sized,
317    {
318        Ok(Pipeline::new(self.factory.create(cfg).await?))
319    }
320}
321
322impl<T, R, C> Clone for ServiceChainFactory<T, R, C>
323where
324    T: Clone,
325{
326    fn clone(&self) -> Self {
327        ServiceChainFactory {
328            factory: self.factory.clone(),
329            _t: PhantomData,
330        }
331    }
332}
333
334impl<T, R, C> fmt::Debug for ServiceChainFactory<T, R, C>
335where
336    T: fmt::Debug,
337{
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        f.debug_struct("ServiceChainFactory")
340            .field("factory", &self.factory)
341            .finish()
342    }
343}
344
345impl<T: ServiceFactory<R, C>, R, C> ServiceFactory<R, C> for ServiceChainFactory<T, R, C> {
346    type Response = T::Response;
347    type Error = T::Error;
348    type Service = T::Service;
349    type InitError = T::InitError;
350
351    #[inline]
352    async fn create(&self, cfg: C) -> Result<Self::Service, Self::InitError> {
353        self.factory.create(cfg).await
354    }
355}