requiem_service/
map.rs

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