actix_service/
apply.rs

1use core::{
2    future::Future,
3    marker::PhantomData,
4    pin::Pin,
5    task::{Context, Poll},
6};
7
8use futures_core::ready;
9use pin_project_lite::pin_project;
10
11use super::{IntoService, IntoServiceFactory, Service, ServiceFactory};
12
13/// Apply transform function to a service.
14///
15/// The In and Out type params refer to the request and response types for the wrapped service.
16pub fn apply_fn<I, S, F, Fut, Req, In, Res, Err>(
17    service: I,
18    wrap_fn: F,
19) -> Apply<S, F, Req, In, Res, Err>
20where
21    I: IntoService<S, In>,
22    S: Service<In, Error = Err>,
23    F: Fn(Req, &S) -> Fut,
24    Fut: Future<Output = Result<Res, Err>>,
25{
26    Apply::new(service.into_service(), wrap_fn)
27}
28
29/// Service factory that produces `apply_fn` service.
30///
31/// The In and Out type params refer to the request and response types for the wrapped service.
32pub fn apply_fn_factory<I, SF, F, Fut, Req, In, Res, Err>(
33    service: I,
34    f: F,
35) -> ApplyFactory<SF, F, Req, In, Res, Err>
36where
37    I: IntoServiceFactory<SF, In>,
38    SF: ServiceFactory<In, Error = Err>,
39    F: Fn(Req, &SF::Service) -> Fut + Clone,
40    Fut: Future<Output = Result<Res, Err>>,
41{
42    ApplyFactory::new(service.into_factory(), f)
43}
44
45/// `Apply` service combinator.
46///
47/// The In and Out type params refer to the request and response types for the wrapped service.
48pub struct Apply<S, F, Req, In, Res, Err>
49where
50    S: Service<In, Error = Err>,
51{
52    service: S,
53    wrap_fn: F,
54    _phantom: PhantomData<fn(Req) -> (In, Res, Err)>,
55}
56
57impl<S, F, Fut, Req, In, Res, Err> Apply<S, F, Req, In, Res, Err>
58where
59    S: Service<In, Error = Err>,
60    F: Fn(Req, &S) -> Fut,
61    Fut: Future<Output = Result<Res, Err>>,
62{
63    /// Create new `Apply` combinator
64    fn new(service: S, wrap_fn: F) -> Self {
65        Self {
66            service,
67            wrap_fn,
68            _phantom: PhantomData,
69        }
70    }
71}
72
73impl<S, F, Fut, Req, In, Res, Err> Clone for Apply<S, F, Req, In, Res, Err>
74where
75    S: Service<In, Error = Err> + Clone,
76    F: Fn(Req, &S) -> Fut + Clone,
77    Fut: Future<Output = Result<Res, Err>>,
78{
79    fn clone(&self) -> Self {
80        Apply {
81            service: self.service.clone(),
82            wrap_fn: self.wrap_fn.clone(),
83            _phantom: PhantomData,
84        }
85    }
86}
87
88impl<S, F, Fut, Req, In, Res, Err> Service<Req> for Apply<S, F, Req, In, Res, Err>
89where
90    S: Service<In, Error = Err>,
91    F: Fn(Req, &S) -> Fut,
92    Fut: Future<Output = Result<Res, Err>>,
93{
94    type Response = Res;
95    type Error = Err;
96    type Future = Fut;
97
98    crate::forward_ready!(service);
99
100    fn call(&self, req: Req) -> Self::Future {
101        (self.wrap_fn)(req, &self.service)
102    }
103}
104
105/// `ApplyFactory` service factory combinator.
106pub struct ApplyFactory<SF, F, Req, In, Res, Err> {
107    factory: SF,
108    wrap_fn: F,
109    _phantom: PhantomData<fn(Req) -> (In, Res, Err)>,
110}
111
112impl<SF, F, Fut, Req, In, Res, Err> ApplyFactory<SF, F, Req, In, Res, Err>
113where
114    SF: ServiceFactory<In, Error = Err>,
115    F: Fn(Req, &SF::Service) -> Fut + Clone,
116    Fut: Future<Output = Result<Res, Err>>,
117{
118    /// Create new `ApplyFactory` new service instance
119    fn new(factory: SF, wrap_fn: F) -> Self {
120        Self {
121            factory,
122            wrap_fn,
123            _phantom: PhantomData,
124        }
125    }
126}
127
128impl<SF, F, Fut, Req, In, Res, Err> Clone for ApplyFactory<SF, F, Req, In, Res, Err>
129where
130    SF: ServiceFactory<In, Error = Err> + Clone,
131    F: Fn(Req, &SF::Service) -> Fut + Clone,
132    Fut: Future<Output = Result<Res, Err>>,
133{
134    fn clone(&self) -> Self {
135        Self {
136            factory: self.factory.clone(),
137            wrap_fn: self.wrap_fn.clone(),
138            _phantom: PhantomData,
139        }
140    }
141}
142
143impl<SF, F, Fut, Req, In, Res, Err> ServiceFactory<Req>
144    for ApplyFactory<SF, F, Req, In, Res, Err>
145where
146    SF: ServiceFactory<In, Error = Err>,
147    F: Fn(Req, &SF::Service) -> Fut + Clone,
148    Fut: Future<Output = Result<Res, Err>>,
149{
150    type Response = Res;
151    type Error = Err;
152
153    type Config = SF::Config;
154    type Service = Apply<SF::Service, F, Req, In, Res, Err>;
155    type InitError = SF::InitError;
156    type Future = ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>;
157
158    fn new_service(&self, cfg: SF::Config) -> Self::Future {
159        let svc = self.factory.new_service(cfg);
160        ApplyServiceFactoryResponse::new(svc, self.wrap_fn.clone())
161    }
162}
163
164pin_project! {
165    pub struct ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
166    where
167        SF: ServiceFactory<In, Error = Err>,
168        F: Fn(Req, &SF::Service) -> Fut,
169        Fut: Future<Output = Result<Res, Err>>,
170    {
171        #[pin]
172        fut: SF::Future,
173        wrap_fn: Option<F>,
174        _phantom: PhantomData<fn(Req) -> Res>,
175    }
176}
177
178impl<SF, F, Fut, Req, In, Res, Err> ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
179where
180    SF: ServiceFactory<In, Error = Err>,
181    F: Fn(Req, &SF::Service) -> Fut,
182    Fut: Future<Output = Result<Res, Err>>,
183{
184    fn new(fut: SF::Future, wrap_fn: F) -> Self {
185        Self {
186            fut,
187            wrap_fn: Some(wrap_fn),
188            _phantom: PhantomData,
189        }
190    }
191}
192
193impl<SF, F, Fut, Req, In, Res, Err> Future
194    for ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
195where
196    SF: ServiceFactory<In, Error = Err>,
197    F: Fn(Req, &SF::Service) -> Fut,
198    Fut: Future<Output = Result<Res, Err>>,
199{
200    type Output = Result<Apply<SF::Service, F, Req, In, Res, Err>, SF::InitError>;
201
202    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
203        let this = self.project();
204
205        let svc = ready!(this.fut.poll(cx))?;
206        Poll::Ready(Ok(Apply::new(svc, this.wrap_fn.take().unwrap())))
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use core::task::Poll;
213
214    use futures_util::future::lazy;
215
216    use super::*;
217    use crate::{
218        ok,
219        pipeline::{pipeline, pipeline_factory},
220        Ready, Service, ServiceFactory,
221    };
222
223    #[derive(Clone)]
224    struct Srv;
225
226    impl Service<()> for Srv {
227        type Response = ();
228        type Error = ();
229        type Future = Ready<Result<(), ()>>;
230
231        crate::always_ready!();
232
233        fn call(&self, _: ()) -> Self::Future {
234            ok(())
235        }
236    }
237
238    #[actix_rt::test]
239    async fn test_call() {
240        let srv = pipeline(apply_fn(Srv, |req: &'static str, srv| {
241            let fut = srv.call(());
242            async move {
243                fut.await.unwrap();
244                Ok((req, ()))
245            }
246        }));
247
248        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
249
250        let res = srv.call("srv").await;
251        assert!(res.is_ok());
252        assert_eq!(res.unwrap(), ("srv", ()));
253    }
254
255    #[actix_rt::test]
256    async fn test_new_service() {
257        let new_srv = pipeline_factory(apply_fn_factory(
258            || ok::<_, ()>(Srv),
259            |req: &'static str, srv| {
260                let fut = srv.call(());
261                async move {
262                    fut.await.unwrap();
263                    Ok((req, ()))
264                }
265            },
266        ));
267
268        let srv = new_srv.new_service(()).await.unwrap();
269
270        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
271
272        let res = srv.call("srv").await;
273        assert!(res.is_ok());
274        assert_eq!(res.unwrap(), ("srv", ()));
275    }
276}