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> for ApplyFactory<SF, F, Req, In, Res, Err>
144where
145    SF: ServiceFactory<In, Error = Err>,
146    F: Fn(Req, &SF::Service) -> Fut + Clone,
147    Fut: Future<Output = Result<Res, Err>>,
148{
149    type Response = Res;
150    type Error = Err;
151
152    type Config = SF::Config;
153    type Service = Apply<SF::Service, F, Req, In, Res, Err>;
154    type InitError = SF::InitError;
155    type Future = ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>;
156
157    fn new_service(&self, cfg: SF::Config) -> Self::Future {
158        let svc = self.factory.new_service(cfg);
159        ApplyServiceFactoryResponse::new(svc, self.wrap_fn.clone())
160    }
161}
162
163pin_project! {
164    pub struct ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
165    where
166        SF: ServiceFactory<In, Error = Err>,
167        F: Fn(Req, &SF::Service) -> Fut,
168        Fut: Future<Output = Result<Res, Err>>,
169    {
170        #[pin]
171        fut: SF::Future,
172        wrap_fn: Option<F>,
173        _phantom: PhantomData<fn(Req) -> Res>,
174    }
175}
176
177impl<SF, F, Fut, Req, In, Res, Err> ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
178where
179    SF: ServiceFactory<In, Error = Err>,
180    F: Fn(Req, &SF::Service) -> Fut,
181    Fut: Future<Output = Result<Res, Err>>,
182{
183    fn new(fut: SF::Future, wrap_fn: F) -> Self {
184        Self {
185            fut,
186            wrap_fn: Some(wrap_fn),
187            _phantom: PhantomData,
188        }
189    }
190}
191
192impl<SF, F, Fut, Req, In, Res, Err> Future
193    for ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
194where
195    SF: ServiceFactory<In, Error = Err>,
196    F: Fn(Req, &SF::Service) -> Fut,
197    Fut: Future<Output = Result<Res, Err>>,
198{
199    type Output = Result<Apply<SF::Service, F, Req, In, Res, Err>, SF::InitError>;
200
201    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
202        let this = self.project();
203
204        let svc = ready!(this.fut.poll(cx))?;
205        Poll::Ready(Ok(Apply::new(svc, this.wrap_fn.take().unwrap())))
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use futures_util::future::lazy;
212
213    use super::*;
214    use crate::{
215        ok,
216        pipeline::{pipeline, pipeline_factory},
217        Ready,
218    };
219
220    #[derive(Clone)]
221    struct Srv;
222
223    impl Service<()> for Srv {
224        type Response = ();
225        type Error = ();
226        type Future = Ready<Result<(), ()>>;
227
228        crate::always_ready!();
229
230        fn call(&self, _: ()) -> Self::Future {
231            ok(())
232        }
233    }
234
235    #[actix_rt::test]
236    async fn test_call() {
237        let srv = pipeline(apply_fn(Srv, |req: &'static str, srv| {
238            let fut = srv.call(());
239            async move {
240                fut.await.unwrap();
241                Ok((req, ()))
242            }
243        }));
244
245        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
246
247        let res = srv.call("srv").await;
248        assert!(res.is_ok());
249        assert_eq!(res.unwrap(), ("srv", ()));
250    }
251
252    #[actix_rt::test]
253    async fn test_new_service() {
254        let new_srv = pipeline_factory(apply_fn_factory(
255            || ok::<_, ()>(Srv),
256            |req: &'static str, srv| {
257                let fut = srv.call(());
258                async move {
259                    fut.await.unwrap();
260                    Ok((req, ()))
261                }
262            },
263        ));
264
265        let srv = new_srv.new_service(()).await.unwrap();
266
267        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
268
269        let res = srv.call("srv").await;
270        assert!(res.is_ok());
271        assert_eq!(res.unwrap(), ("srv", ()));
272    }
273}