skyzen_core/
responder.rs

1use alloc::borrow::Cow;
2use alloc::boxed::Box;
3#[cfg(feature = "openapi")]
4use alloc::collections::BTreeMap;
5use alloc::string::String;
6use alloc::vec::Vec;
7use core::convert::Infallible;
8use core::pin::Pin;
9use http_kit::error::BoxHttpError;
10use http_kit::header::{HeaderMap, HeaderName, HeaderValue};
11use http_kit::HttpError;
12use http_kit::{
13    utils::{AsyncBufRead, ByteStr, Bytes},
14    Body, Request, Response,
15};
16
17#[cfg(feature = "openapi")]
18use crate::openapi::{ResponseSchema, SchemaRef};
19use crate::Error;
20
21/// Transform a object into a part of HTTP response,always is response body,header,etc.
22pub trait Responder: Sized + Send + Sync + 'static {
23    /// Error type returned when responding fails.
24    type Error: HttpError;
25    /// Modify the response,sometime also read the request (but the body may have already been consumed).
26    ///
27    /// # Errors
28    ///
29    /// Returns an error if the response fails.
30    fn respond_to(self, _request: &Request, response: &mut Response) -> Result<(), Self::Error>;
31
32    /// Describe the responder's `OpenAPI` schemas, if available.
33    #[cfg(feature = "openapi")]
34    #[must_use]
35    fn openapi() -> Option<Vec<ResponseSchema>> {
36        None
37    }
38
39    /// Register dependent schemas into the `OpenAPI` components map.
40    #[cfg(feature = "openapi")]
41    fn register_openapi_schemas(_defs: &mut BTreeMap<String, SchemaRef>) {}
42}
43
44macro_rules! impl_tuple_responder {
45        ($($ty:ident),*) => {
46            const _:() = {
47                    // To prevent these macro-generated errors from overwhelming users.
48            #[doc(hidden)]
49            pub enum TupleResponderError<$($ty:Responder),*> {
50                $($ty(<$ty as Responder>::Error),)*
51            }
52
53            impl <$($ty: Responder),*>core::fmt::Display for TupleResponderError<$($ty),*> {
54                #[allow(unused_variables)]
55                fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56                    match self {
57                        $(TupleResponderError::$ty(e) => write!(f,"{}",e),)*
58                        #[allow(unreachable_patterns)]
59                        _ => unreachable!(),
60                    }
61                }
62            }
63
64            impl<$($ty: Responder),*>core::fmt::Debug for TupleResponderError<$($ty),*> {
65                #[allow(unused_variables)]
66                fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
67                    match self {
68                        $(TupleResponderError::$ty(e) => write!(f,"{:?}",e),)*
69                        #[allow(unreachable_patterns)]
70                        _ => unreachable!(),
71                    }
72                }
73            }
74
75            impl <$($ty: Responder),*>core::error::Error for TupleResponderError<$($ty),*> {}
76
77            impl<$($ty: Responder),*>http_kit::HttpError for TupleResponderError<$($ty),*> {
78                fn status(&self) -> http_kit::StatusCode { {
79                    match self {
80                        $(TupleResponderError::$ty(e) => e.status(),)*
81                        #[allow(unreachable_patterns)]
82                        _ => unreachable!(),
83                    }
84                } }
85            }
86
87
88                #[allow(non_snake_case)]
89                #[allow(unused_variables)]
90                impl <$($ty:Responder,)*>Responder for ($($ty,)*){
91                    type Error=TupleResponderError<$($ty),*>;
92                    fn respond_to(self, request: &Request,response:&mut Response) -> Result<(),Self::Error>{
93                        let ($($ty,)*)=self;
94                        $($ty.respond_to(request,response).map_err(|e| TupleResponderError::$ty(e))?;)*
95                        Ok(())
96                    }
97
98                    #[cfg(feature = "openapi")]
99                    fn openapi() -> Option<Vec<ResponseSchema>> {
100                        #[allow(unused_mut)]
101                        let mut schemas = Vec::new();
102                        $(
103                            if let Some(mut inner) = <$ty as Responder>::openapi() {
104                                schemas.append(&mut inner);
105                            }
106                        )*
107                        if schemas.is_empty() { None } else { Some(schemas) }
108                    }
109
110                    #[cfg(feature = "openapi")]
111                    fn register_openapi_schemas(defs: &mut BTreeMap<String, SchemaRef>) {
112                        $(
113                            <$ty as Responder>::register_openapi_schemas(defs);
114                        )*
115                    }
116                }
117            };
118        };
119}
120
121tuples!(impl_tuple_responder);
122
123impl_base_responder![Bytes, Vec<u8>, Body, &'static [u8], Cow<'static, [u8]>];
124
125impl Responder for Pin<Box<dyn AsyncBufRead + Send + Sync + 'static>> {
126    type Error = core::convert::Infallible;
127    fn respond_to(self, _request: &Request, response: &mut Response) -> Result<(), Self::Error> {
128        *response.body_mut() = Body::from_reader(self, None);
129        Ok(())
130    }
131}
132
133impl_base_utf8_responder![ByteStr, String, &'static str, Cow<'static, str>];
134
135impl Responder for Response {
136    type Error = core::convert::Infallible;
137    fn respond_to(self, _request: &Request, response: &mut Response) -> Result<(), Self::Error> {
138        *response = self;
139        Ok(())
140    }
141
142    #[cfg(feature = "openapi")]
143    fn openapi() -> Option<Vec<ResponseSchema>> {
144        Some(vec![ResponseSchema {
145            status: None,
146            description: None,
147            schema: None,
148            content_type: None,
149        }])
150    }
151}
152
153impl<T: Responder, E: HttpError> Responder for core::result::Result<T, E> {
154    type Error = BoxHttpError;
155    fn respond_to(self, request: &Request, response: &mut Response) -> Result<(), Self::Error> {
156        match self {
157            Ok(responder) => responder
158                .respond_to(request, response)
159                .map_err(|e| Box::new(e) as BoxHttpError),
160            Err(e) => Err(Box::new(e) as BoxHttpError),
161        }
162    }
163
164    #[cfg(feature = "openapi")]
165    fn openapi() -> Option<Vec<ResponseSchema>> {
166        let mut schemas = Vec::new();
167        if let Some(mut inner) = T::openapi() {
168            schemas.append(&mut inner);
169        }
170        schemas.push(ResponseSchema {
171            status: Some(http_kit::StatusCode::SERVICE_UNAVAILABLE),
172            description: None,
173            schema: None,
174            content_type: None,
175        });
176        if schemas.is_empty() {
177            None
178        } else {
179            Some(schemas)
180        }
181    }
182
183    #[cfg(feature = "openapi")]
184    fn register_openapi_schemas(defs: &mut BTreeMap<String, SchemaRef>) {
185        T::register_openapi_schemas(defs);
186    }
187}
188
189impl<T: Responder> Responder for core::result::Result<T, Error> {
190    type Error = BoxHttpError;
191    fn respond_to(self, request: &Request, response: &mut Response) -> Result<(), Self::Error> {
192        match self {
193            Ok(responder) => responder
194                .respond_to(request, response)
195                .map_err(|e| Box::new(e) as BoxHttpError),
196            Err(e) => Err(e.into_boxed_http_error()),
197        }
198    }
199
200    #[cfg(feature = "openapi")]
201    fn openapi() -> Option<Vec<ResponseSchema>> {
202        let mut schemas = Vec::new();
203        if let Some(mut inner) = T::openapi() {
204            schemas.append(&mut inner);
205        }
206        if schemas.is_empty() {
207            None
208        } else {
209            Some(schemas)
210        }
211    }
212
213    #[cfg(feature = "openapi")]
214    fn register_openapi_schemas(defs: &mut BTreeMap<String, SchemaRef>) {
215        T::register_openapi_schemas(defs);
216    }
217}
218
219impl Responder for HeaderMap {
220    type Error = Infallible;
221    fn respond_to(self, _request: &Request, response: &mut Response) -> Result<(), Self::Error> {
222        response.headers_mut().extend(self);
223        Ok(())
224    }
225
226    #[cfg(feature = "openapi")]
227    fn openapi() -> Option<Vec<ResponseSchema>> {
228        Some(vec![ResponseSchema {
229            status: None,
230            description: None,
231            schema: None,
232            content_type: None,
233        }])
234    }
235}
236
237impl Responder for (HeaderName, HeaderValue) {
238    type Error = Infallible;
239    fn respond_to(self, _request: &Request, response: &mut Response) -> Result<(), Self::Error> {
240        let (key, value) = self;
241        response.headers_mut().append(key, value);
242        Ok(())
243    }
244
245    #[cfg(feature = "openapi")]
246    fn openapi() -> Option<Vec<ResponseSchema>> {
247        Some(vec![ResponseSchema {
248            status: None,
249            description: None,
250            schema: None,
251            content_type: None,
252        }])
253    }
254}