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
9pub type BoxedError = Box<dyn StdError + Send + Sync>;
11
12#[derive(Debug)]
14#[non_exhaustive]
15pub enum Error {
16 Hyper(hyper::Error),
18 HttpParse(ParseError),
20 HttpStatus(StatusError),
22 Io(IoError),
24 SerdeJson(serde_json::Error),
26 InvalidUri(http::uri::InvalidUri),
28 #[cfg(feature = "quinn")]
29 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
30 H3Connection(salvo_http3::error::ConnectionError),
32 #[cfg(feature = "quinn")]
33 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
34 H3Stream(salvo_http3::error::StreamError),
36 #[cfg(feature = "quinn")]
37 #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
38 H3SendDatagram(h3_datagram::datagram_handler::SendDatagramError),
40 #[cfg(feature = "anyhow")]
42 #[cfg_attr(docsrs, doc(cfg(feature = "anyhow")))]
43 Anyhow(anyhow::Error),
44 #[cfg(feature = "eyre")]
46 #[cfg_attr(docsrs, doc(cfg(feature = "eyre")))]
47 Eyre(eyre::Report),
48 Other(BoxedError),
50}
51
52impl Error {
53 #[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}