Skip to main content

xitca_web/middleware/
eraser.rs

1//! type eraser middleware.
2
3use core::marker::PhantomData;
4
5use crate::service::Service;
6
7#[doc(hidden)]
8mod marker {
9    pub struct EraseReqBody;
10    pub struct EraseResBody;
11
12    pub struct EraseErr;
13}
14
15use marker::*;
16
17/// Type eraser middleware is for erasing "unwanted" complex types produced by service tree
18/// and expose well known concrete types `xitca-web` can handle.
19///
20/// # Example
21/// ```rust
22/// # use xitca_web::{
23/// #   handler::handler_service,
24/// #   middleware::{eraser::TypeEraser, limit::Limit, Group},
25/// #   service::ServiceExt,
26/// #   App, WebContext
27/// #   };
28/// // a handler function expect xitca_web::body::RequestBody as body type.
29/// async fn handler(_: &WebContext<'_>) -> &'static str {
30///     "hello,world!"
31/// }
32///
33/// // a limit middleware that limit request body to max size of 1MB.
34/// // this middleware would produce a new type of request body that
35/// // handler function don't know of.
36/// let limit = Limit::new().set_request_body_max_size(1024 * 1024);
37///
38/// // an eraser middleware that transform any request body to xitca_web::body::RequestBody.
39/// let eraser = TypeEraser::request_body();
40///
41/// App::new()
42///     .at("/", handler_service(handler))
43///     // introduce eraser middleware between handler and limit middleware
44///     // to resolve the type difference between them.
45///     // without it this piece of code would simply refuse to compile.
46///     .enclosed(eraser.clone())
47///     .enclosed(limit.clone());
48///
49/// // group middleware is suggested way of handling of use case like this.
50/// let group = Group::new().enclosed(eraser.clone()).enclosed(limit.clone());
51///
52/// // it's suggested to group multiple type mutation middlewares together and apply
53/// // eraser on them once if possible. reason being TypeErase has a cost and by
54/// // grouping you only pay for it once.
55/// let group = group.enclosed(limit);
56///
57/// App::new()
58///     .at("/", handler_service(handler))
59///     .enclosed(group);
60/// ```
61pub struct TypeEraser<M>(PhantomData<M>);
62
63impl<M> Clone for TypeEraser<M> {
64    fn clone(&self) -> Self {
65        Self(PhantomData)
66    }
67}
68
69impl TypeEraser<EraseReqBody> {
70    /// Erase generic request body type. making downstream middlewares observe [`RequestBody`].
71    ///
72    /// [`RequestBody`]: crate::body::RequestBody
73    pub const fn request_body() -> Self {
74        TypeEraser(PhantomData)
75    }
76}
77
78impl TypeEraser<EraseResBody> {
79    /// Erase generic response body type. making downstream middlewares observe [`ResponseBody`].
80    ///
81    /// [`ResponseBody`]: crate::body::ResponseBody
82    pub const fn response_body() -> Self {
83        TypeEraser(PhantomData)
84    }
85}
86
87impl TypeEraser<EraseErr> {
88    /// Erase generic E type from Service<Error = E>. making downstream middlewares observe [`Error`].
89    ///
90    /// [`Error`]: crate::error::Error
91    pub const fn error() -> Self {
92        TypeEraser(PhantomData)
93    }
94}
95
96impl<M, S, E> Service<Result<S, E>> for TypeEraser<M> {
97    type Response = service::EraserService<M, S>;
98    type Error = E;
99
100    async fn call(&self, res: Result<S, E>) -> Result<Self::Response, Self::Error> {
101        res.map(|service| service::EraserService {
102            service,
103            _erase: PhantomData,
104        })
105    }
106}
107
108mod service {
109    use core::cell::RefCell;
110
111    use crate::{
112        WebContext, body::BodyStream, body::ResponseBody, error::Error, http::WebResponse, service::ready::ReadyService,
113    };
114
115    use super::*;
116
117    pub struct EraserService<M, S> {
118        pub(super) service: S,
119        pub(super) _erase: PhantomData<M>,
120    }
121
122    impl<'r, S, C, ReqB, ResB, Err> Service<WebContext<'r, C, ReqB>> for EraserService<EraseReqBody, S>
123    where
124        S: for<'rs> Service<WebContext<'rs, C>, Response = WebResponse<ResB>, Error = Err>,
125        ReqB: BodyStream + Default + 'static,
126        ResB: BodyStream + 'static,
127    {
128        type Response = WebResponse;
129        type Error = Err;
130
131        async fn call(&self, mut ctx: WebContext<'r, C, ReqB>) -> Result<Self::Response, Self::Error> {
132            let body = ctx.take_body_mut();
133            let body = crate::body::downcast_body(body);
134            let mut body = RefCell::new(body);
135            let WebContext { req, ctx, .. } = ctx;
136            let res = self.service.call(WebContext::new(req, &mut body, ctx)).await?;
137            Ok(res.map(ResponseBody::boxed))
138        }
139    }
140
141    impl<S, Req, ResB> Service<Req> for EraserService<EraseResBody, S>
142    where
143        S: Service<Req, Response = WebResponse<ResB>>,
144        ResB: BodyStream + 'static,
145    {
146        type Response = WebResponse;
147        type Error = S::Error;
148
149        #[inline]
150        async fn call(&self, req: Req) -> Result<Self::Response, Self::Error> {
151            let res = self.service.call(req).await?;
152            Ok(res.map(ResponseBody::boxed))
153        }
154    }
155
156    impl<'r, C, B, S> Service<WebContext<'r, C, B>> for EraserService<EraseErr, S>
157    where
158        S: Service<WebContext<'r, C, B>>,
159        S::Error: Into<Error>,
160    {
161        type Response = S::Response;
162        type Error = Error;
163
164        #[inline]
165        async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
166            self.service.call(ctx).await.map_err(Into::into)
167        }
168    }
169
170    impl<M, S> ReadyService for EraserService<M, S>
171    where
172        S: ReadyService,
173    {
174        type Ready = S::Ready;
175
176        #[inline]
177        async fn ready(&self) -> Self::Ready {
178            self.service.ready().await
179        }
180    }
181}
182
183#[cfg(test)]
184mod test {
185    use xitca_unsafe_collection::futures::NowOrPanic;
186
187    use crate::{
188        App, WebContext,
189        body::Full,
190        bytes::Bytes,
191        error::Error,
192        handler::handler_service,
193        http::{Request, StatusCode, WebResponse},
194        middleware::Group,
195        service::ServiceExt,
196    };
197
198    use super::*;
199
200    async fn handler(_: &WebContext<'_>) -> &'static str {
201        "996"
202    }
203
204    async fn map_body<S, C, B, Err>(_: &S, _: WebContext<'_, C, B>) -> Result<WebResponse<Full<Bytes>>, Err>
205    where
206        S: for<'r> Service<WebContext<'r, C, B>, Response = WebResponse, Error = Err>,
207    {
208        Ok(WebResponse::new(Full::new(Bytes::new())))
209    }
210
211    async fn middleware_fn<S, C, B, Err>(s: &S, ctx: WebContext<'_, C, B>) -> Result<WebResponse, Err>
212    where
213        S: for<'r> Service<WebContext<'r, C, B>, Response = WebResponse, Error = Err>,
214    {
215        s.call(ctx).await
216    }
217
218    #[test]
219    fn erase_body() {
220        let _ = App::new()
221            // map WebResponse to WebResponse<Once<Bytes>> type.
222            .at("/", handler_service(handler).enclosed_fn(map_body))
223            // erase the body type to make it WebResponse type again.
224            .enclosed(TypeEraser::response_body())
225            // observe erased body type.
226            .enclosed_fn(middleware_fn)
227            .finish()
228            .call(())
229            .now_or_panic()
230            .unwrap()
231            .call(Request::default())
232            .now_or_panic()
233            .unwrap();
234    }
235
236    #[test]
237    fn erase_error() {
238        async fn middleware_fn<S, C, B, Err>(s: &S, ctx: WebContext<'_, C, B>) -> Result<WebResponse, StatusCode>
239        where
240            S: for<'r> Service<WebContext<'r, C, B>, Response = WebResponse, Error = Err>,
241        {
242            s.call(ctx).await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
243        }
244
245        async fn middleware_fn2<S, C, B>(s: &S, ctx: WebContext<'_, C, B>) -> Result<WebResponse, Error>
246        where
247            S: for<'r> Service<WebContext<'r, C, B>, Response = WebResponse, Error = Error>,
248        {
249            s.call(ctx).await
250        }
251
252        let _ = App::new()
253            // map WebResponse to WebResponse<Once<Bytes>> type.
254            .at("/", handler_service(handler).enclosed(TypeEraser::error()))
255            .enclosed(
256                Group::new()
257                    .enclosed_fn(middleware_fn)
258                    .enclosed(TypeEraser::error())
259                    .enclosed_fn(middleware_fn2),
260            )
261            .finish()
262            .call(())
263            .now_or_panic()
264            .unwrap()
265            .call(Request::default())
266            .now_or_panic()
267            .unwrap();
268    }
269}