predawn_core/
error.rs

1use std::{error::Error as StdError, fmt};
2
3use http::{header::CONTENT_TYPE, HeaderValue, StatusCode};
4use mime::TEXT_PLAIN_UTF_8;
5
6use crate::{location::Location, response::Response, response_error::ResponseError};
7
8/// Alias for a type-erased error type.
9pub type BoxError = Box<dyn StdError + Send + Sync>;
10
11#[derive(Debug)]
12pub struct Error {
13    response: Response,
14    inner: BoxError,
15    error_stack: Box<[Box<str>]>,
16}
17
18impl Error {
19    pub fn is<T>(&self) -> bool
20    where
21        T: StdError + 'static,
22    {
23        self.inner.is::<T>()
24    }
25
26    pub fn downcast_ref<T>(&self) -> Option<&T>
27    where
28        T: StdError + 'static,
29    {
30        self.inner.downcast_ref::<T>()
31    }
32
33    #[allow(clippy::type_complexity)]
34    pub fn downcast<T>(self) -> Result<(Response, T, Box<[Box<str>]>), Self>
35    where
36        T: StdError + 'static,
37    {
38        let Self {
39            response,
40            inner,
41            error_stack,
42        } = self;
43
44        match inner.downcast::<T>() {
45            Ok(err) => Ok((response, *err, error_stack)),
46            Err(err) => Err(Self {
47                response,
48                inner: err,
49                error_stack,
50            }),
51        }
52    }
53
54    pub fn status(&self) -> StatusCode {
55        self.response.status()
56    }
57
58    pub fn response(self) -> Response {
59        self.response
60    }
61
62    pub fn error_stack(&self) -> &[Box<str>] {
63        &self.error_stack
64    }
65}
66
67impl<T> From<T> for Error
68where
69    T: ResponseError,
70{
71    fn from(error: T) -> Self {
72        let response = error.as_response();
73        let error_stack = error.error_stack();
74
75        let inner = error.inner();
76
77        Self {
78            response,
79            inner,
80            error_stack,
81        }
82    }
83}
84
85impl From<(StatusCode, BoxError)> for Error {
86    #[track_caller]
87    fn from((status, mut error): (StatusCode, BoxError)) -> Self {
88        loop {
89            match error.downcast::<Error>() {
90                Ok(o) => {
91                    if o.inner.is::<Error>() {
92                        error = o.inner;
93                    } else {
94                        return *o;
95                    }
96                }
97                Err(e) => {
98                    error = e;
99                    break;
100                }
101            }
102        }
103
104        let response = Response::builder()
105            .status(status)
106            .header(
107                CONTENT_TYPE,
108                HeaderValue::from_static(TEXT_PLAIN_UTF_8.as_ref()),
109            )
110            .body(error.to_string().into())
111            .unwrap();
112
113        let mut stack = Vec::new();
114        stack.push(format!("0: {}, at: {}", error, Location::caller()).into_boxed_str());
115
116        Self {
117            response,
118            inner: error,
119            error_stack: stack.into_boxed_slice(),
120        }
121    }
122}
123
124impl From<BoxError> for Error {
125    #[track_caller]
126    #[inline]
127    fn from(error: BoxError) -> Self {
128        Error::from((StatusCode::INTERNAL_SERVER_ERROR, error))
129    }
130}
131
132impl fmt::Display for Error {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        fmt::Display::fmt(&self.inner, f)
135    }
136}
137
138impl StdError for Error {
139    fn source(&self) -> Option<&(dyn StdError + 'static)> {
140        Some(&*self.inner)
141    }
142}
143
144impl AsRef<dyn StdError + Send + Sync> for Error {
145    fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) {
146        &*self.inner
147    }
148}
149
150impl AsRef<dyn StdError> for Error {
151    fn as_ref(&self) -> &(dyn StdError + 'static) {
152        &*self.inner
153    }
154}