1use futures_util::future::FusedFuture;
2use std::fmt::{Debug, Display};
3use std::future::Future;
4use std::pin::Pin;
5use std::task::{Context, Poll};
6
7use hyper::{Body, Response, StatusCode};
8use log::{debug, trace};
9
10use crate::handler::IntoResponse;
11use crate::helpers::http::response::create_empty_response;
12use crate::state::{request_id, State};
13
14#[derive(Debug)]
17pub struct HandlerError {
18 status_code: StatusCode,
19 cause: anyhow::Error,
20}
21
22impl<E> From<E> for HandlerError
25where
26 E: Into<anyhow::Error> + Display,
27{
28 fn from(error: E) -> HandlerError {
29 trace!(" converting Error to HandlerError: {}", error);
30
31 HandlerError {
32 status_code: StatusCode::INTERNAL_SERVER_ERROR,
33 cause: error.into(),
34 }
35 }
36}
37
38impl HandlerError {
39 pub fn status(&self) -> StatusCode {
41 self.status_code
42 }
43
44 pub fn with_status(self, status_code: StatusCode) -> HandlerError {
78 HandlerError {
79 status_code,
80 ..self
81 }
82 }
83
84 pub fn cause(&self) -> &anyhow::Error {
86 &self.cause
87 }
88
89 pub fn into_cause(self) -> anyhow::Error {
91 self.cause
92 }
93
94 pub fn downcast_cause_ref<E>(&self) -> Option<&E>
96 where
97 E: Display + Debug + Send + Sync + 'static,
98 {
99 self.cause.downcast_ref()
100 }
101
102 pub fn downcast_cause_mut<E>(&mut self) -> Option<&mut E>
104 where
105 E: Display + Debug + Send + Sync + 'static,
106 {
107 self.cause.downcast_mut()
108 }
109}
110
111impl IntoResponse for HandlerError {
112 fn into_response(self, state: &State) -> Response<Body> {
113 debug!(
114 "[{}] HandlerError generating {} {} response: {}",
115 request_id(state),
116 self.status_code.as_u16(),
117 self.status_code
118 .canonical_reason()
119 .unwrap_or("(unregistered)",),
120 self.cause
121 );
122
123 create_empty_response(state, self.status_code)
124 }
125}
126
127pub trait MapHandlerError<T> {
155 fn map_err_with_status(self, status_code: StatusCode) -> Result<T, HandlerError>;
157}
158
159impl<T, E> MapHandlerError<T> for Result<T, E>
160where
161 E: Into<anyhow::Error> + Display,
162{
163 fn map_err_with_status(self, status_code: StatusCode) -> Result<T, HandlerError> {
164 self.map_err(|err| {
165 trace!(" converting Error to HandlerError: {}", err);
166 HandlerError {
167 status_code,
168 cause: err.into(),
169 }
170 })
171 }
172}
173
174#[pin_project::pin_project(project = MapErrWithStatusProj, project_replace = MapErrWithStatusProjOwn)]
176#[derive(Debug)]
177#[must_use = "futures do nothing unless you `.await` or poll them"]
178pub enum MapErrWithStatus<F> {
179 Incomplete {
180 #[pin]
181 future: F,
182 status: StatusCode,
183 },
184 Complete,
185}
186
187impl<F> MapErrWithStatus<F> {
188 fn new(future: F, status: StatusCode) -> Self {
189 Self::Incomplete { future, status }
190 }
191}
192
193impl<F, T, E> FusedFuture for MapErrWithStatus<F>
194where
195 F: Future<Output = Result<T, E>>,
196 E: Into<anyhow::Error> + Display,
197{
198 fn is_terminated(&self) -> bool {
199 matches!(self, Self::Complete)
200 }
201}
202
203impl<F, T, E> Future for MapErrWithStatus<F>
204where
205 F: Future<Output = Result<T, E>>,
206 E: Into<anyhow::Error> + Display,
207{
208 type Output = Result<T, HandlerError>;
209
210 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
211 match self.as_mut().project() {
212 MapErrWithStatusProj::Incomplete { future, .. } => {
213 let output = match future.poll(cx) {
214 Poll::Ready(output) => output,
215 Poll::Pending => return Poll::Pending,
216 };
217 match self.project_replace(MapErrWithStatus::Complete) {
218 MapErrWithStatusProjOwn::Incomplete { status, .. } => {
219 Poll::Ready(output.map_err_with_status(status))
220 }
221 MapErrWithStatusProjOwn::Complete => unreachable!(),
222 }
223 }
224 MapErrWithStatusProj::Complete => {
225 panic!("MapErrWithStatus must not be polled after it returned `Poll::Ready`")
226 }
227 }
228 }
229}
230
231pub trait MapHandlerErrorFuture {
258 fn map_err_with_status(self, status_code: StatusCode) -> MapErrWithStatus<Self>
260 where
261 Self: Sized;
262}
263
264impl<T, E, F> MapHandlerErrorFuture for F
265where
266 E: Into<anyhow::Error> + Display,
267 F: Future<Output = Result<T, E>>,
268{
269 fn map_err_with_status(self, status_code: StatusCode) -> MapErrWithStatus<Self> {
270 MapErrWithStatus::new(self, status_code)
271 }
272}
273
274#[cfg(test)]
275mod test {
276 use super::*;
277 use std::io;
278 use thiserror::Error;
279
280 #[derive(Debug, Error)]
281 #[error("Dummy Error")]
282 struct DummyError;
283
284 fn error_prone() -> Result<(), HandlerError> {
285 Err(DummyError.into())
286 }
287
288 #[test]
289 fn test_error_downcast() {
290 let mut err = error_prone().unwrap_err();
291 assert!(err.downcast_cause_ref::<DummyError>().is_some());
292 assert!(err.downcast_cause_mut::<DummyError>().is_some());
293 assert!(err.downcast_cause_ref::<io::Error>().is_none());
294 assert!(err.downcast_cause_mut::<io::Error>().is_none());
295 }
296}