salvo_core/
error.rs

1use std::convert::Infallible;
2use std::error::Error as StdError;
3use std::fmt::{self, Display, Formatter};
4use std::io::Error as IoError;
5
6use crate::http::{ParseError, StatusError};
7use crate::{Response, Scribe};
8
9/// `BoxedError` is a boxed error type that can be used as a trait object.
10pub type BoxedError = Box<dyn StdError + Send + Sync>;
11
12/// Errors that can happen inside salvo.
13#[derive(Debug)]
14#[non_exhaustive]
15pub enum Error {
16    /// Error occurred in hyper.
17    Hyper(hyper::Error),
18    /// Error happened when parse http.
19    HttpParse(ParseError),
20    /// Error from http response error status.
21    HttpStatus(StatusError),
22    /// Std I/O error.
23    Io(IoError),
24    /// SerdeJson error.
25    SerdeJson(serde_json::Error),
26    /// Invalid URI error.
27    InvalidUri(http::uri::InvalidUri),
28    #[cfg(feature = "quinn")]
29    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
30    /// H3 ConnectionError.
31    H3Connection(salvo_http3::error::ConnectionError),
32    #[cfg(feature = "quinn")]
33    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
34    /// H3 StreamError.
35    H3Stream(salvo_http3::error::StreamError),
36    #[cfg(feature = "quinn")]
37    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
38    /// H3 SendDatagramError.
39    H3SendDatagram(h3_datagram::datagram_handler::SendDatagramError),
40    /// Anyhow error.
41    #[cfg(feature = "anyhow")]
42    #[cfg_attr(docsrs, doc(cfg(feature = "anyhow")))]
43    Anyhow(anyhow::Error),
44    /// Anyhow error.
45    #[cfg(feature = "eyre")]
46    #[cfg_attr(docsrs, doc(cfg(feature = "eyre")))]
47    Eyre(eyre::Report),
48    /// Custom error that does not fall under any other error kind.
49    Other(BoxedError),
50}
51
52impl Error {
53    /// Create a custom error.
54    #[inline]
55    pub fn other(error: impl Into<BoxedError>) -> Self {
56        Self::Other(error.into())
57    }
58}
59impl Display for Error {
60    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
61        match self {
62            Self::Hyper(e) => Display::fmt(e, f),
63            Self::HttpParse(e) => Display::fmt(e, f),
64            Self::HttpStatus(e) => Display::fmt(e, f),
65            Self::Io(e) => Display::fmt(e, f),
66            Self::SerdeJson(e) => Display::fmt(e, f),
67            Self::InvalidUri(e) => Display::fmt(e, f),
68            #[cfg(feature = "quinn")]
69            Self::H3Connection(e) => Display::fmt(e, f),
70            #[cfg(feature = "quinn")]
71            Self::H3Stream(e) => Display::fmt(e, f),
72            #[cfg(feature = "quinn")]
73            Self::H3SendDatagram(e) => Display::fmt(e, f),
74            #[cfg(feature = "anyhow")]
75            Self::Anyhow(e) => Display::fmt(e, f),
76            #[cfg(feature = "eyre")]
77            Self::Eyre(e) => Display::fmt(e, f),
78            Self::Other(e) => Display::fmt(e, f),
79        }
80    }
81}
82
83impl StdError for Error {}
84
85impl From<Infallible> for Error {
86    #[inline]
87    fn from(infallible: Infallible) -> Self {
88        match infallible {}
89    }
90}
91impl From<hyper::Error> for Error {
92    #[inline]
93    fn from(e: hyper::Error) -> Self {
94        Self::Hyper(e)
95    }
96}
97impl From<ParseError> for Error {
98    #[inline]
99    fn from(d: ParseError) -> Self {
100        Self::HttpParse(d)
101    }
102}
103impl From<StatusError> for Error {
104    #[inline]
105    fn from(e: StatusError) -> Self {
106        Self::HttpStatus(e)
107    }
108}
109impl From<IoError> for Error {
110    #[inline]
111    fn from(e: IoError) -> Self {
112        Self::Io(e)
113    }
114}
115impl From<http::uri::InvalidUri> for Error {
116    #[inline]
117    fn from(e: http::uri::InvalidUri) -> Self {
118        Self::InvalidUri(e)
119    }
120}
121impl From<serde_json::Error> for Error {
122    #[inline]
123    fn from(e: serde_json::Error) -> Self {
124        Self::SerdeJson(e)
125    }
126}
127cfg_feature! {
128    #![feature = "quinn"]
129    impl From<salvo_http3::error::ConnectionError> for Error {
130        #[inline]
131        fn from(e: salvo_http3::error::ConnectionError) -> Self {
132            Self::H3Connection(e)
133        }
134    }
135    impl From<salvo_http3::error::StreamError> for Error {
136        #[inline]
137        fn from(e: salvo_http3::error::StreamError) -> Self {
138            Self::H3Stream(e)
139        }
140    }
141    impl From<h3_datagram::datagram_handler::SendDatagramError> for Error {
142        #[inline]
143        fn from(e: h3_datagram::datagram_handler::SendDatagramError) -> Self {
144            Self::H3SendDatagram(e)
145        }
146    }
147}
148cfg_feature! {
149    #![feature = "anyhow"]
150    impl From<anyhow::Error> for Error {
151        #[inline]
152        fn from(e: anyhow::Error) -> Self {
153            Self::Anyhow(e)
154        }
155    }
156}
157cfg_feature! {
158    #![feature = "eyre"]
159    impl From<eyre::Report> for Error {
160        #[inline]
161        fn from(e: eyre::Report) -> Self {
162            Self::Eyre(e)
163        }
164    }
165}
166
167impl From<BoxedError> for Error {
168    #[inline]
169    fn from(e: BoxedError) -> Self {
170        Self::Other(e)
171    }
172}
173
174impl Scribe for Error {
175    fn render(self, res: &mut Response) {
176        let status_error = match self {
177            Self::HttpStatus(e) => e,
178            _ => StatusError::internal_server_error().cause(self),
179        };
180        res.render(status_error);
181    }
182}
183cfg_feature! {
184    #![feature = "anyhow"]
185    impl Scribe for anyhow::Error {
186        #[inline]
187        fn render(self, res: &mut Response) {
188            tracing::error!(error = ?self, "anyhow error occurred");
189            res.render(StatusError::internal_server_error().origin(self));
190        }
191    }
192}
193cfg_feature! {
194    #![feature = "eyre"]
195    impl Scribe for eyre::Report {
196        #[inline]
197        fn render(self, res: &mut Response) {
198            tracing::error!(error = ?self, "eyre error occurred");
199            res.render(StatusError::internal_server_error().cause(self));
200        }
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use crate::http::*;
207    use crate::{Depot, Writer};
208    use std::str::FromStr;
209
210    use super::*;
211
212    #[tokio::test]
213    #[cfg(feature = "anyhow")]
214    async fn test_anyhow() {
215        let mut req = Request::default();
216        let mut res = Response::default();
217        let mut depot = Depot::new();
218        let e: anyhow::Error = anyhow::anyhow!("detail message");
219        e.write(&mut req, &mut depot, &mut res).await;
220        assert_eq!(res.status_code, Some(StatusCode::INTERNAL_SERVER_ERROR));
221    }
222
223    #[tokio::test]
224    #[cfg(feature = "eyre")]
225    async fn test_eyre() {
226        let mut req = Request::default();
227        let mut res = Response::default();
228        let mut depot = Depot::new();
229        let e: eyre::Report = eyre::Report::msg("detail message");
230        e.write(&mut req, &mut depot, &mut res).await;
231        assert_eq!(res.status_code, Some(StatusCode::INTERNAL_SERVER_ERROR));
232    }
233
234    #[tokio::test]
235    async fn test_error() {
236        let mut req = Request::default();
237        let mut res = Response::default();
238        let mut depot = Depot::new();
239
240        let e = Error::Other(Box::new(std::io::Error::new(
241            std::io::ErrorKind::Other,
242            "detail message",
243        )));
244        e.write(&mut req, &mut depot, &mut res).await;
245        assert_eq!(res.status_code, Some(StatusCode::INTERNAL_SERVER_ERROR));
246    }
247
248    #[test]
249    fn test_error_from() {
250        use std::io;
251
252        let err: Error = io::Error::new(io::ErrorKind::Other, "oh no!").into();
253        assert!(matches!(err, Error::Io(_)));
254
255        let err: Error = ParseError::ParseFromStr.into();
256        assert!(matches!(err, Error::HttpParse(_)));
257
258        let err: Error = StatusError::bad_request().into();
259        assert!(matches!(err, Error::HttpStatus(_)));
260
261        let err: Error = serde_json::from_str::<serde_json::Value>("{")
262            .unwrap_err()
263            .into();
264        assert!(matches!(err, Error::SerdeJson(_)));
265
266        let err: Error = http::Uri::from_str("ht tp://host.com").unwrap_err().into();
267        assert!(matches!(err, Error::InvalidUri(_)));
268
269        let err: Error = Error::other(Box::new(std::io::Error::new(
270            std::io::ErrorKind::Other,
271            "custom error",
272        )));
273        assert!(matches!(err, Error::Other(_)));
274    }
275
276    #[test]
277    fn test_error_display() {
278        use std::io;
279
280        let err: Error = io::Error::new(io::ErrorKind::Other, "io error").into();
281        assert_eq!(format!("{}", err), "io error");
282
283        let err: Error = ParseError::ParseFromStr.into();
284        assert_eq!(format!("{}", err), "Parse error when parse from str.");
285
286        let err: Error = StatusError::bad_request().brief("status error").into();
287        assert!(format!("{}", err).contains("status error"));
288    }
289
290    #[tokio::test]
291    async fn test_error_scribe() {
292        let mut req = Request::default();
293        let mut res = Response::default();
294        let mut depot = Depot::new();
295
296        let e = Error::from(StatusError::bad_request());
297        e.write(&mut req, &mut depot, &mut res).await;
298        assert_eq!(res.status_code, Some(StatusCode::BAD_REQUEST));
299
300        let mut res = Response::default();
301        let e = std::io::Error::new(std::io::ErrorKind::Other, "io error");
302        Error::from(e).write(&mut req, &mut depot, &mut res).await;
303        assert_eq!(res.status_code, Some(StatusCode::INTERNAL_SERVER_ERROR));
304    }
305}