xitca_web/handler/
impls.rs

1use core::net::SocketAddr;
2
3use crate::{
4    body::ResponseBody,
5    context::WebContext,
6    error::{Error, ErrorStatus},
7    http::{Method, RequestExt, StatusCode, WebRequest, WebResponse},
8};
9
10use super::{FromRequest, Responder};
11
12impl<'a, 'r, C, B, T, E> FromRequest<'a, WebContext<'r, C, B>> for Result<T, E>
13where
14    T: FromRequest<'a, WebContext<'r, C, B>, Error = E>,
15{
16    type Type<'b> = Result<T::Type<'b>, T::Error>;
17    type Error = Error;
18
19    #[inline]
20    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
21        Ok(T::from_request(ctx).await)
22    }
23}
24
25impl<'a, 'r, C, B, T> FromRequest<'a, WebContext<'r, C, B>> for Option<T>
26where
27    T: FromRequest<'a, WebContext<'r, C, B>>,
28{
29    type Type<'b> = Option<T::Type<'b>>;
30    type Error = Error;
31
32    #[inline]
33    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
34        Ok(T::from_request(ctx).await.ok())
35    }
36}
37
38impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for &'a WebContext<'a, C, B>
39where
40    C: 'static,
41    B: 'static,
42{
43    type Type<'b> = &'b WebContext<'b, C, B>;
44    type Error = Error;
45
46    #[inline]
47    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
48        Ok(ctx)
49    }
50}
51
52impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for &'a WebRequest<()> {
53    type Type<'b> = &'b WebRequest<()>;
54    type Error = Error;
55
56    #[inline]
57    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
58        Ok(ctx.req())
59    }
60}
61
62impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for WebRequest<()> {
63    type Type<'b> = WebRequest<()>;
64    type Error = Error;
65
66    #[inline]
67    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
68        Ok(ctx.req().clone())
69    }
70}
71
72impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for &'a RequestExt<()> {
73    type Type<'b> = &'b RequestExt<()>;
74    type Error = Error;
75
76    #[inline]
77    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
78        Ok(ctx.req().body())
79    }
80}
81
82impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for RequestExt<()> {
83    type Type<'b> = RequestExt<()>;
84    type Error = Error;
85
86    #[inline]
87    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
88        Ok(ctx.req().body().clone())
89    }
90}
91
92impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for &'a SocketAddr {
93    type Type<'b> = &'b SocketAddr;
94    type Error = Error;
95
96    #[inline]
97    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
98        Ok(ctx.req().body().socket_addr())
99    }
100}
101
102impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for SocketAddr {
103    type Type<'b> = SocketAddr;
104    type Error = Error;
105
106    #[inline]
107    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
108        Ok(*ctx.req().body().socket_addr())
109    }
110}
111
112impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for &'a Method {
113    type Type<'b> = &'b Method;
114    type Error = Error;
115
116    #[inline]
117    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
118        Ok(ctx.req().method())
119    }
120}
121
122impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for Method {
123    type Type<'b> = Method;
124    type Error = Error;
125
126    #[inline]
127    async fn from_request(ctx: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
128        Ok(ctx.req().method().clone())
129    }
130}
131
132impl<'a, 'r, C, B> FromRequest<'a, WebContext<'r, C, B>> for () {
133    type Type<'b> = ();
134    type Error = Error;
135
136    #[inline]
137    async fn from_request(_: &'a WebContext<'r, C, B>) -> Result<Self, Self::Error> {
138        Ok(())
139    }
140}
141
142impl<'r, C, B, T, Res, Err, E> Responder<WebContext<'r, C, B>> for Result<T, E>
143where
144    T: for<'r2> Responder<WebContext<'r2, C, B>, Response = Res, Error = Err>,
145    Error: From<E> + From<Err>,
146{
147    type Response = Res;
148    type Error = Error;
149
150    #[inline]
151    async fn respond(self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
152        self?.respond(ctx).await.map_err(Into::into)
153    }
154}
155
156impl<'r, C, B, ResB> Responder<WebContext<'r, C, B>> for WebResponse<ResB> {
157    type Response = WebResponse<ResB>;
158    type Error = Error;
159
160    #[inline]
161    async fn respond(self, _: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
162        Ok(self)
163    }
164}
165
166impl<'r, C, B> Responder<WebContext<'r, C, B>> for Error {
167    type Response = WebResponse;
168    type Error = Error;
169
170    #[inline]
171    async fn respond(self, _: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
172        Err(self)
173    }
174}
175
176impl<'r, C, B> Responder<WebContext<'r, C, B>> for ErrorStatus {
177    type Response = WebResponse;
178    type Error = Error;
179
180    #[inline]
181    async fn respond(self, _: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
182        Err(Error::from(self))
183    }
184}
185
186impl<'r, C, B> Responder<WebContext<'r, C, B>> for StatusCode {
187    type Response = WebResponse;
188    type Error = Error;
189
190    async fn respond(self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
191        let res = ctx.into_response(ResponseBody::empty());
192        Responder::<WebContext<'r, C, B>>::map(self, res)
193    }
194
195    fn map(self, mut res: Self::Response) -> Result<Self::Response, Self::Error> {
196        *res.status_mut() = self;
197        Ok(res)
198    }
199}
200
201// shared error impl for serde enabled features: json, urlencoded, etc.
202#[cfg(feature = "serde")]
203const _: () = {
204    crate::error::error_from_service!(serde::de::value::Error);
205    crate::error::forward_blank_bad_request!(serde::de::value::Error);
206};
207
208#[cfg(test)]
209mod test {
210    use xitca_unsafe_collection::futures::NowOrPanic;
211
212    use crate::http::header::{CONTENT_TYPE, COOKIE, HeaderMap, HeaderValue};
213
214    use super::*;
215
216    #[test]
217    fn extract_default_impls() {
218        let mut req = WebContext::new_test(());
219        let req = req.as_web_ctx();
220
221        Option::<()>::from_request(&req).now_or_panic().unwrap().unwrap();
222
223        Result::<(), Error>::from_request(&req).now_or_panic().unwrap().unwrap();
224
225        <&WebContext<'_>>::from_request(&req).now_or_panic().unwrap();
226
227        <()>::from_request(&req).now_or_panic().unwrap();
228    }
229
230    #[test]
231    fn respond_chain() {
232        let mut req = WebContext::new_test(());
233        let mut req = req.as_web_ctx();
234
235        let mut headers = HeaderMap::new();
236        headers.insert(COOKIE, HeaderValue::from_static("none"));
237
238        let check_res = |res: WebResponse| {
239            assert_eq!(res.status(), StatusCode::ACCEPTED);
240            assert_eq!(res.headers().get(COOKIE).unwrap().to_str().unwrap(), "none");
241            assert_eq!(
242                res.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap(),
243                "text/plain; charset=utf-8"
244            );
245        };
246
247        let res = (StatusCode::ACCEPTED, headers.clone(), "hello,world!")
248            .respond(req.reborrow())
249            .now_or_panic()
250            .unwrap();
251        check_res(res);
252
253        let res = ("hello,world!", StatusCode::ACCEPTED, headers.clone())
254            .respond(req.reborrow())
255            .now_or_panic()
256            .unwrap();
257        check_res(res);
258
259        let res = (headers, "hello,world!", StatusCode::ACCEPTED)
260            .respond(req)
261            .now_or_panic()
262            .unwrap();
263        check_res(res);
264    }
265}