actix_service/
map.rs

1use core::{
2    future::Future,
3    marker::PhantomData,
4    pin::Pin,
5    task::{Context, Poll},
6};
7
8use pin_project_lite::pin_project;
9
10use super::{Service, ServiceFactory};
11
12/// Service for the `map` combinator, changing the type of a service's response.
13///
14/// This is created by the `ServiceExt::map` method.
15pub struct Map<A, F, Req, Res> {
16    service: A,
17    f: F,
18    _t: PhantomData<fn(Req) -> Res>,
19}
20
21impl<A, F, Req, Res> Map<A, F, Req, Res> {
22    /// Create new `Map` combinator
23    pub(crate) fn new(service: A, f: F) -> Self
24    where
25        A: Service<Req>,
26        F: FnMut(A::Response) -> Res,
27    {
28        Self {
29            service,
30            f,
31            _t: PhantomData,
32        }
33    }
34}
35
36impl<A, F, Req, Res> Clone for Map<A, F, Req, Res>
37where
38    A: Clone,
39    F: Clone,
40{
41    fn clone(&self) -> Self {
42        Map {
43            service: self.service.clone(),
44            f: self.f.clone(),
45            _t: PhantomData,
46        }
47    }
48}
49
50impl<A, F, Req, Res> Service<Req> for Map<A, F, Req, Res>
51where
52    A: Service<Req>,
53    F: FnMut(A::Response) -> Res + Clone,
54{
55    type Response = Res;
56    type Error = A::Error;
57    type Future = MapFuture<A, F, Req, Res>;
58
59    crate::forward_ready!(service);
60
61    fn call(&self, req: Req) -> Self::Future {
62        MapFuture::new(self.service.call(req), self.f.clone())
63    }
64}
65
66pin_project! {
67    pub struct MapFuture<A, F, Req, Res>
68    where
69        A: Service<Req>,
70        F: FnMut(A::Response) -> Res,
71    {
72        f: F,
73        #[pin]
74        fut: A::Future,
75    }
76}
77
78impl<A, F, Req, Res> MapFuture<A, F, Req, Res>
79where
80    A: Service<Req>,
81    F: FnMut(A::Response) -> Res,
82{
83    fn new(fut: A::Future, f: F) -> Self {
84        MapFuture { f, fut }
85    }
86}
87
88impl<A, F, Req, Res> Future for MapFuture<A, F, Req, Res>
89where
90    A: Service<Req>,
91    F: FnMut(A::Response) -> Res,
92{
93    type Output = Result<Res, A::Error>;
94
95    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
96        let this = self.project();
97
98        match this.fut.poll(cx) {
99            Poll::Ready(Ok(resp)) => Poll::Ready(Ok((this.f)(resp))),
100            Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
101            Poll::Pending => Poll::Pending,
102        }
103    }
104}
105
106/// `MapNewService` new service combinator
107pub struct MapServiceFactory<A, F, Req, Res> {
108    a: A,
109    f: F,
110    r: PhantomData<fn(Req) -> Res>,
111}
112
113impl<A, F, Req, Res> MapServiceFactory<A, F, Req, Res> {
114    /// Create new `Map` new service instance
115    pub(crate) fn new(a: A, f: F) -> Self
116    where
117        A: ServiceFactory<Req>,
118        F: FnMut(A::Response) -> Res,
119    {
120        Self {
121            a,
122            f,
123            r: PhantomData,
124        }
125    }
126}
127
128impl<A, F, Req, Res> Clone for MapServiceFactory<A, F, Req, Res>
129where
130    A: Clone,
131    F: Clone,
132{
133    fn clone(&self) -> Self {
134        Self {
135            a: self.a.clone(),
136            f: self.f.clone(),
137            r: PhantomData,
138        }
139    }
140}
141
142impl<A, F, Req, Res> ServiceFactory<Req> for MapServiceFactory<A, F, Req, Res>
143where
144    A: ServiceFactory<Req>,
145    F: FnMut(A::Response) -> Res + Clone,
146{
147    type Response = Res;
148    type Error = A::Error;
149
150    type Config = A::Config;
151    type Service = Map<A::Service, F, Req, Res>;
152    type InitError = A::InitError;
153    type Future = MapServiceFuture<A, F, Req, Res>;
154
155    fn new_service(&self, cfg: A::Config) -> Self::Future {
156        MapServiceFuture::new(self.a.new_service(cfg), self.f.clone())
157    }
158}
159
160pin_project! {
161    pub struct MapServiceFuture<A, F, Req, Res>
162    where
163        A: ServiceFactory<Req>,
164        F: FnMut(A::Response) -> Res,
165    {
166        #[pin]
167        fut: A::Future,
168        f: Option<F>,
169    }
170}
171
172impl<A, F, Req, Res> MapServiceFuture<A, F, Req, Res>
173where
174    A: ServiceFactory<Req>,
175    F: FnMut(A::Response) -> Res,
176{
177    fn new(fut: A::Future, f: F) -> Self {
178        MapServiceFuture { f: Some(f), fut }
179    }
180}
181
182impl<A, F, Req, Res> Future for MapServiceFuture<A, F, Req, Res>
183where
184    A: ServiceFactory<Req>,
185    F: FnMut(A::Response) -> Res,
186{
187    type Output = Result<Map<A::Service, F, Req, Res>, A::InitError>;
188
189    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
190        let this = self.project();
191
192        if let Poll::Ready(svc) = this.fut.poll(cx)? {
193            Poll::Ready(Ok(Map::new(svc, this.f.take().unwrap())))
194        } else {
195            Poll::Pending
196        }
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use futures_util::future::lazy;
203
204    use super::*;
205    use crate::{ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
206
207    struct Srv;
208
209    impl Service<()> for Srv {
210        type Response = ();
211        type Error = ();
212        type Future = Ready<Result<(), ()>>;
213
214        crate::always_ready!();
215
216        fn call(&self, _: ()) -> Self::Future {
217            ok(())
218        }
219    }
220
221    #[actix_rt::test]
222    async fn test_poll_ready() {
223        let srv = Srv.map(|_| "ok");
224        let res = lazy(|cx| srv.poll_ready(cx)).await;
225        assert_eq!(res, Poll::Ready(Ok(())));
226    }
227
228    #[actix_rt::test]
229    async fn test_call() {
230        let srv = Srv.map(|_| "ok");
231        let res = srv.call(()).await;
232        assert!(res.is_ok());
233        assert_eq!(res.unwrap(), "ok");
234    }
235
236    #[actix_rt::test]
237    async fn test_new_service() {
238        let new_srv = (|| ok::<_, ()>(Srv)).into_factory().map(|_| "ok");
239        let srv = new_srv.new_service(&()).await.unwrap();
240        let res = srv.call(()).await;
241        assert!(res.is_ok());
242        assert_eq!(res.unwrap(), ("ok"));
243    }
244}