poem/
error.rs

1//! Some common error types.
2
3use std::{
4    convert::Infallible,
5    error::Error as StdError,
6    fmt::{self, Debug, Display, Formatter},
7    string::FromUtf8Error,
8};
9
10use headers::{ContentRange, HeaderMapExt};
11use http::{Extensions, Method};
12
13use crate::{http::StatusCode, IntoResponse, Response};
14
15macro_rules! define_http_error {
16    ($($(#[$docs:meta])* ($name:ident, $status:ident);)*) => {
17        $(
18        $(#[$docs])*
19        #[allow(non_snake_case)]
20        #[inline]
21        pub fn $name(err: impl StdError + Send + Sync + 'static) -> Error {
22            Error::new(err, StatusCode::$status)
23        }
24        )*
25    };
26}
27
28/// Represents a type that can be converted to [`Error`].
29pub trait ResponseError {
30    /// The status code of this error.
31    fn status(&self) -> StatusCode;
32
33    /// Convert this error to a HTTP response.
34    fn as_response(&self) -> Response
35    where
36        Self: StdError + Send + Sync + 'static,
37    {
38        let mut resp = self.to_string().into_response();
39        resp.set_status(self.status());
40        resp
41    }
42}
43
44enum ErrorSource {
45    BoxedError(Box<dyn StdError + Send + Sync>),
46    #[cfg(feature = "anyhow")]
47    Anyhow(anyhow::Error),
48    #[cfg(feature = "eyre06")]
49    Eyre06(eyre06::Report),
50}
51
52impl Debug for ErrorSource {
53    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
54        match self {
55            ErrorSource::BoxedError(err) => Debug::fmt(err, f),
56            #[cfg(feature = "anyhow")]
57            ErrorSource::Anyhow(err) => Debug::fmt(err, f),
58            #[cfg(feature = "eyre06")]
59            ErrorSource::Eyre06(err) => Debug::fmt(err, f),
60        }
61    }
62}
63
64type AsResponseFn = fn(&Error) -> Response;
65type GetStatusFn = fn(&Error) -> StatusCode;
66
67enum AsResponse {
68    Status(StatusCode),
69    Fn(AsResponseFn, GetStatusFn),
70    Response(Response),
71}
72
73impl AsResponse {
74    #[inline]
75    fn from_status(status: StatusCode) -> Self {
76        AsResponse::Status(status)
77    }
78
79    fn from_type<T: ResponseError + StdError + Send + Sync + 'static>() -> Self {
80        AsResponse::Fn(
81            |err| {
82                let err = err.downcast_ref::<T>().expect("valid error");
83                err.as_response()
84            },
85            |err| {
86                let err = err.downcast_ref::<T>().expect("valid error");
87                err.status()
88            },
89        )
90    }
91}
92
93/// General response error.
94///
95/// # Create from any error types
96///
97/// ```
98/// use poem::{error::InternalServerError, handler, Result};
99///
100/// #[handler]
101/// async fn index() -> Result<String> {
102///     Ok(std::fs::read_to_string("example.txt").map_err(InternalServerError)?)
103/// }
104/// ```
105///
106/// # Create you own error type
107///
108/// ```
109/// use poem::{error::ResponseError, handler, http::StatusCode, Endpoint, Request, Result};
110///
111/// #[derive(Debug, thiserror::Error)]
112/// #[error("my error")]
113/// struct MyError;
114///
115/// impl ResponseError for MyError {
116///     fn status(&self) -> StatusCode {
117///         StatusCode::BAD_GATEWAY
118///     }
119/// }
120///
121/// fn do_something() -> Result<(), MyError> {
122///     Err(MyError)
123/// }
124///
125/// #[handler]
126/// async fn index() -> Result<()> {
127///     Ok(do_something()?)
128/// }
129///
130/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
131/// let resp = index.get_response(Request::default()).await;
132/// assert_eq!(resp.status(), StatusCode::BAD_GATEWAY);
133/// assert_eq!(resp.into_body().into_string().await.unwrap(), "my error");
134/// # });
135/// ```
136///
137/// # Custom error response
138///
139/// ```
140/// use poem::{error::ResponseError, handler, http::StatusCode, Response, Result, Request, Body, Endpoint};
141///
142/// #[derive(Debug, thiserror::Error)]
143/// #[error("my error")]
144/// struct MyError;
145///
146/// impl ResponseError for MyError {
147///     fn status(&self) -> StatusCode {
148///         StatusCode::BAD_GATEWAY
149///     }
150///
151///     fn as_response(&self) -> Response {
152///         let body = Body::from_json(serde_json::json!({
153///             "code": 1000,
154///             "message": self.to_string(),
155///         })).unwrap();
156///         Response::builder().status(self.status()).body(body)
157///     }
158/// }
159///
160/// fn do_something() -> Result<(), MyError> {
161///     Err(MyError)
162/// }
163///
164/// #[handler]
165/// async fn index() -> Result<()> {
166///     Ok(do_something()?)
167/// }
168///
169/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
170/// let resp = index.get_response(Request::default()).await;
171/// assert_eq!(resp.status(), StatusCode::BAD_GATEWAY);
172/// assert_eq!(resp.into_body().into_json::<serde_json::Value>().await.unwrap(),
173/// serde_json::json!({
174///     "code": 1000,
175///     "message": "my error",
176/// }));
177/// # });
178/// ```
179///
180/// # Downcast the error to concrete error type
181/// ```
182/// use poem::{error::NotFoundError, Error};
183///
184/// let err: Error = NotFoundError.into();
185///
186/// assert!(err.is::<NotFoundError>());
187/// assert_eq!(err.downcast_ref::<NotFoundError>(), Some(&NotFoundError));
188/// ```
189pub struct Error {
190    as_response: AsResponse,
191    source: Option<ErrorSource>,
192    extensions: Extensions,
193    msg: Option<String>,
194}
195
196impl Debug for Error {
197    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
198        f.debug_struct("Error")
199            .field("source", &self.source)
200            .finish()
201    }
202}
203
204impl Display for Error {
205    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
206        if let Some(msg) = &self.msg {
207            return write!(f, "{msg}");
208        }
209
210        match &self.source {
211            Some(ErrorSource::BoxedError(err)) => Display::fmt(err, f),
212            #[cfg(feature = "anyhow")]
213            Some(ErrorSource::Anyhow(err)) => write!(f, "{err:#}"),
214            #[cfg(feature = "eyre06")]
215            Some(ErrorSource::Eyre06(err)) => Display::fmt(err, f),
216            None => write!(f, "{}", self.status()),
217        }
218    }
219}
220
221impl StdError for Error {
222    fn source(&self) -> Option<&(dyn StdError + 'static)> {
223        match &self.source {
224            Some(ErrorSource::BoxedError(err)) => Some(err.as_ref()),
225            #[cfg(feature = "anyhow")]
226            Some(ErrorSource::Anyhow(err)) => Some(err.as_ref()),
227            #[cfg(feature = "eyre06")]
228            Some(ErrorSource::Eyre06(err)) => Some(err.as_ref()),
229            None => None,
230        }
231    }
232}
233
234impl From<Infallible> for Error {
235    fn from(_: Infallible) -> Self {
236        unreachable!()
237    }
238}
239
240impl<T: ResponseError + StdError + Send + Sync + 'static> From<T> for Error {
241    fn from(err: T) -> Self {
242        Error {
243            as_response: AsResponse::from_type::<T>(),
244            source: Some(ErrorSource::BoxedError(Box::new(err))),
245            extensions: Extensions::default(),
246            msg: None,
247        }
248    }
249}
250
251impl From<Box<dyn StdError + Send + Sync>> for Error {
252    fn from(err: Box<dyn StdError + Send + Sync>) -> Self {
253        (StatusCode::INTERNAL_SERVER_ERROR, err).into()
254    }
255}
256
257impl From<(StatusCode, Box<dyn StdError + Send + Sync>)> for Error {
258    fn from((status, err): (StatusCode, Box<dyn StdError + Send + Sync>)) -> Self {
259        Error {
260            as_response: AsResponse::from_status(status),
261            source: Some(ErrorSource::BoxedError(err)),
262            extensions: Extensions::default(),
263            msg: None,
264        }
265    }
266}
267
268#[cfg(feature = "anyhow")]
269impl From<anyhow::Error> for Error {
270    fn from(err: anyhow::Error) -> Self {
271        Error {
272            as_response: AsResponse::from_status(StatusCode::INTERNAL_SERVER_ERROR),
273            source: Some(ErrorSource::Anyhow(err)),
274            extensions: Extensions::default(),
275            msg: None,
276        }
277    }
278}
279
280#[cfg(feature = "eyre06")]
281impl From<eyre06::Error> for Error {
282    fn from(err: eyre06::Error) -> Self {
283        Error {
284            as_response: AsResponse::from_status(StatusCode::INTERNAL_SERVER_ERROR),
285            source: Some(ErrorSource::Eyre06(err)),
286            extensions: Extensions::default(),
287            msg: None,
288        }
289    }
290}
291
292#[cfg(feature = "anyhow")]
293impl From<(StatusCode, anyhow::Error)> for Error {
294    fn from((status, err): (StatusCode, anyhow::Error)) -> Self {
295        Error {
296            as_response: AsResponse::from_status(status),
297            source: Some(ErrorSource::Anyhow(err)),
298            extensions: Extensions::default(),
299            msg: None,
300        }
301    }
302}
303
304#[cfg(feature = "eyre06")]
305impl From<(StatusCode, eyre06::Report)> for Error {
306    fn from((status, err): (StatusCode, eyre06::Report)) -> Self {
307        Error {
308            as_response: AsResponse::from_status(status),
309            source: Some(ErrorSource::Eyre06(err)),
310            extensions: Extensions::default(),
311            msg: None,
312        }
313    }
314}
315
316impl From<StatusCode> for Error {
317    fn from(status: StatusCode) -> Self {
318        Error::from_status(status)
319    }
320}
321
322impl Error {
323    /// Create a new error object from any error type with a status code.
324    #[inline]
325    pub fn new<T: StdError + Send + Sync + 'static>(err: T, status: StatusCode) -> Self {
326        Self {
327            as_response: AsResponse::from_status(status),
328            source: Some(ErrorSource::BoxedError(Box::new(err))),
329            extensions: Extensions::default(),
330            msg: None,
331        }
332    }
333
334    /// Create a new error object from response.
335    pub fn from_response(resp: Response) -> Self {
336        Self {
337            as_response: AsResponse::Response(resp),
338            source: None,
339            extensions: Extensions::default(),
340            msg: None,
341        }
342    }
343
344    /// create a new error object from status code.
345    pub fn from_status(status: StatusCode) -> Self {
346        #[derive(Debug, thiserror::Error)]
347        #[error("{0}")]
348        struct StatusError(StatusCode);
349
350        impl ResponseError for StatusError {
351            fn status(&self) -> StatusCode {
352                self.0
353            }
354
355            fn as_response(&self) -> Response
356            where
357                Self: StdError + Send + Sync + 'static,
358            {
359                self.0.into_response()
360            }
361        }
362
363        StatusError(status).into()
364    }
365
366    /// Create a new error object from a string with a status code.
367    pub fn from_string(msg: impl Into<String>, status: StatusCode) -> Self {
368        #[derive(Debug, thiserror::Error)]
369        #[error("{0}")]
370        struct StringError(String);
371
372        Self::new(StringError(msg.into()), status)
373    }
374
375    /// Downcast this error object by reference.
376    #[inline]
377    pub fn downcast_ref<T: StdError + Send + Sync + 'static>(&self) -> Option<&T> {
378        match &self.source {
379            Some(ErrorSource::BoxedError(err)) => err.downcast_ref::<T>(),
380            #[cfg(feature = "anyhow")]
381            Some(ErrorSource::Anyhow(err)) => err.downcast_ref::<T>(),
382            #[cfg(feature = "eyre06")]
383            Some(ErrorSource::Eyre06(err)) => err.downcast_ref::<T>(),
384            None => None,
385        }
386    }
387
388    /// Attempts to downcast the error to a concrete error type.
389    #[inline]
390    pub fn downcast<T: StdError + Send + Sync + 'static>(self) -> Result<T, Error> {
391        let as_response = self.as_response;
392        let extensions = self.extensions;
393        let msg = self.msg;
394
395        match self.source {
396            Some(ErrorSource::BoxedError(err)) => match err.downcast::<T>() {
397                Ok(err) => Ok(*err),
398                Err(err) => Err(Error {
399                    as_response,
400                    source: Some(ErrorSource::BoxedError(err)),
401                    extensions,
402                    msg,
403                }),
404            },
405            #[cfg(feature = "anyhow")]
406            Some(ErrorSource::Anyhow(err)) => match err.downcast::<T>() {
407                Ok(err) => Ok(err),
408                Err(err) => Err(Error {
409                    as_response,
410                    source: Some(ErrorSource::Anyhow(err)),
411                    extensions,
412                    msg,
413                }),
414            },
415            #[cfg(feature = "eyre06")]
416            Some(ErrorSource::Eyre06(err)) => match err.downcast::<T>() {
417                Ok(err) => Ok(err),
418                Err(err) => Err(Error {
419                    as_response,
420                    source: Some(ErrorSource::Eyre06(err)),
421                    extensions,
422                    msg,
423                }),
424            },
425            None => Err(Error {
426                as_response,
427                source: None,
428                extensions,
429                msg,
430            }),
431        }
432    }
433
434    /// Returns `true` if the error type is the same as `T`.
435    #[inline]
436    pub fn is<T: StdError + Debug + Send + Sync + 'static>(&self) -> bool {
437        match &self.source {
438            Some(ErrorSource::BoxedError(err)) => err.is::<T>(),
439            #[cfg(feature = "anyhow")]
440            Some(ErrorSource::Anyhow(err)) => err.is::<T>(),
441            #[cfg(feature = "eyre06")]
442            Some(ErrorSource::Eyre06(err)) => err.is::<T>(),
443            None => false,
444        }
445    }
446
447    /// Consumes this to return a response object.
448    pub fn into_response(self) -> Response {
449        let mut resp = match self.as_response {
450            AsResponse::Status(status) => Response::builder().status(status).body(self.to_string()),
451            AsResponse::Fn(ref f, _) => f(&self),
452            AsResponse::Response(resp) => resp,
453        };
454        *resp.extensions_mut() = self.extensions;
455        resp
456    }
457
458    /// Returns whether the error has a source or not.
459    pub fn has_source(&self) -> bool {
460        self.source.is_some()
461    }
462
463    /// Inserts a value to extensions
464    ///
465    /// Passed to `Response::extensions` when this error converted to
466    /// [`Response`].
467    ///
468    /// # Examples
469    ///
470    /// ```rust
471    /// # use poem::{http::StatusCode, Error};
472    /// let mut err = Error::from_status(StatusCode::BAD_REQUEST);
473    /// err.set_data(100i32);
474    ///
475    /// let resp = err.into_response();
476    /// assert_eq!(resp.data::<i32>(), Some(&100));
477    /// ```
478    #[inline]
479    pub fn set_data(&mut self, data: impl Clone + Send + Sync + 'static) {
480        self.extensions.insert(data);
481    }
482
483    /// Get a reference from extensions
484    pub fn data<T: Send + Sync + 'static>(&self) -> Option<&T> {
485        self.extensions.get()
486    }
487
488    /// Get the status code of the error
489    pub fn status(&self) -> StatusCode {
490        match &self.as_response {
491            AsResponse::Status(status) => *status,
492            AsResponse::Fn(_, get_status) => (get_status)(self),
493            AsResponse::Response(resp) => resp.status(),
494        }
495    }
496
497    /// Returns `true` if the error was created from the response
498    #[inline]
499    pub fn is_from_response(&self) -> bool {
500        matches!(&self.as_response, AsResponse::Response(_))
501    }
502
503    /// Set the error message
504    pub fn set_error_message(&mut self, msg: impl Into<String>) {
505        self.msg = Some(msg.into());
506    }
507}
508
509define_http_error!(
510    /// Wraps any error into [`Error`] and the status code is [`StatusCode::BAD_REQUEST`].
511    (BadRequest, BAD_REQUEST);
512    /// Wraps any error into [`Error`] and the status code is [`StatusCode::UNAUTHORIZED`].
513    (Unauthorized, UNAUTHORIZED);
514    /// Wraps any error into [`Error`] and the status code is [`StatusCode::PAYMENT_REQUIRED`].
515    (PaymentRequired, PAYMENT_REQUIRED);
516    /// Wraps any error into [`Error`] and the status code is [`StatusCode::FORBIDDEN`].
517    (Forbidden, FORBIDDEN);
518    /// Wraps any error into [`Error`] and the status code is [`StatusCode::NOT_FOUND`].
519    (NotFound, NOT_FOUND);
520    /// Wraps any error into [`Error`] and the status code is [`StatusCode::METHOD_NOT_ALLOWED`].
521    (MethodNotAllowed, METHOD_NOT_ALLOWED);
522    /// Wraps any error into [`Error`] and the status code is [`StatusCode::NOT_ACCEPTABLE`].
523    (NotAcceptable, NOT_ACCEPTABLE);
524    /// Wraps any error into [`Error`] and the status code is [`StatusCode::PROXY_AUTHENTICATION_REQUIRED`].
525    (ProxyAuthenticationRequired, PROXY_AUTHENTICATION_REQUIRED);
526    /// Wraps any error into [`Error`] and the status code is [`StatusCode::REQUEST_TIMEOUT`].
527    (RequestTimeout, REQUEST_TIMEOUT);
528    /// Wraps any error into [`Error`] and the status code is [`StatusCode::CONFLICT`].
529    (Conflict, CONFLICT);
530    /// Wraps any error into [`Error`] and the status code is [`StatusCode::GONE`].
531    (Gone, GONE);
532    /// Wraps any error into [`Error`] and the status code is [`StatusCode::LENGTH_REQUIRED`].
533    (LengthRequired, LENGTH_REQUIRED);
534    /// Wraps any error into [`Error`] and the status code is [`StatusCode::PAYLOAD_TOO_LARGE`].
535    (PayloadTooLarge, PAYLOAD_TOO_LARGE);
536    /// Wraps any error into [`Error`] and the status code is [`StatusCode::URI_TOO_LONG`].
537    (UriTooLong, URI_TOO_LONG);
538    /// Wraps any error into [`Error`] and the status code is [`StatusCode::UNSUPPORTED_MEDIA_TYPE`].
539    (UnsupportedMediaType, UNSUPPORTED_MEDIA_TYPE);
540    /// Wraps any error into [`Error`] and the status code is [`StatusCode::RANGE_NOT_SATISFIABLE`].
541    (RangeNotSatisfiable, RANGE_NOT_SATISFIABLE);
542    /// Wraps any error into [`Error`] and the status code is [`StatusCode::IM_A_TEAPOT`].
543    (ImATeapot, IM_A_TEAPOT);
544    /// Wraps any error into [`Error`] and the status code is [`StatusCode::MISDIRECTED_REQUEST`].
545    (MisdirectedRequest, MISDIRECTED_REQUEST);
546    /// Wraps any error into [`Error`] and the status code is [`StatusCode::UNPROCESSABLE_ENTITY`].
547    (UnprocessableEntity, UNPROCESSABLE_ENTITY);
548    /// Wraps any error into [`Error`] and the status code is [`StatusCode::LOCKED`].
549    (Locked, LOCKED);
550    /// Wraps any error into [`Error`] and the status code is [`StatusCode::FAILED_DEPENDENCY`].
551    (FailedDependency, FAILED_DEPENDENCY);
552    /// Wraps any error into [`Error`] and the status code is [`StatusCode::UPGRADE_REQUIRED`].
553    (UpgradeRequired, UPGRADE_REQUIRED);
554    /// Wraps any error into [`Error`] and the status code is [`StatusCode::PRECONDITION_FAILED`].
555    (PreconditionFailed, PRECONDITION_FAILED);
556    /// Wraps any error into [`Error`] and the status code is [`StatusCode::PRECONDITION_REQUIRED`].
557    (PreconditionRequired, PRECONDITION_REQUIRED);
558    /// Wraps any error into [`Error`] and the status code is [`StatusCode::TOO_MANY_REQUESTS`].
559    (TooManyRequests, TOO_MANY_REQUESTS);
560    /// Wraps any error into [`Error`] and the status code is [`StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE`].
561    (RequestHeaderFieldsTooLarge, REQUEST_HEADER_FIELDS_TOO_LARGE);
562    /// Wraps any error into [`Error`] and the status code is [`StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS`].
563    (UnavailableForLegalReasons, UNAVAILABLE_FOR_LEGAL_REASONS);
564    /// Wraps any error into [`Error`] and the status code is [`StatusCode::EXPECTATION_FAILED`].
565    (ExpectationFailed, EXPECTATION_FAILED);
566    /// Wraps any error into [`Error`] and the status code is [`StatusCode::INTERNAL_SERVER_ERROR`].
567    (InternalServerError, INTERNAL_SERVER_ERROR);
568    /// Wraps any error into [`Error`] and the status code is [`StatusCode::NOT_IMPLEMENTED`].
569    (NotImplemented, NOT_IMPLEMENTED);
570    /// Wraps any error into [`Error`] and the status code is [`StatusCode::BAD_GATEWAY`].
571    (BadGateway, BAD_GATEWAY);
572    /// Wraps any error into [`Error`] and the status code is [`StatusCode::SERVICE_UNAVAILABLE`].
573    (ServiceUnavailable, SERVICE_UNAVAILABLE);
574    /// Wraps any error into [`Error`] and the status code is [`StatusCode::GATEWAY_TIMEOUT`].
575    (GatewayTimeout, GATEWAY_TIMEOUT);
576    /// Wraps any error into [`Error`] and the status code is [`StatusCode::HTTP_VERSION_NOT_SUPPORTED`].
577    (HttpVersionNotSupported, HTTP_VERSION_NOT_SUPPORTED);
578    /// Wraps any error into [`Error`] and the status code is [`StatusCode::VARIANT_ALSO_NEGOTIATES`].
579    (VariantAlsoNegotiates, VARIANT_ALSO_NEGOTIATES);
580    /// Wraps any error into [`Error`] and the status code is [`StatusCode::INSUFFICIENT_STORAGE`].
581    (InsufficientStorage, INSUFFICIENT_STORAGE);
582    /// Wraps any error into [`Error`] and the status code is [`StatusCode::LOOP_DETECTED`].
583    (LoopDetected, LOOP_DETECTED);
584    /// Wraps any error into [`Error`] and the status code is [`StatusCode::NOT_EXTENDED`].
585    (NotExtended, NOT_EXTENDED);
586    /// Wraps any error into [`Error`] and the status code is [`StatusCode::NETWORK_AUTHENTICATION_REQUIRED`].
587    (NetworkAuthenticationRequired, NETWORK_AUTHENTICATION_REQUIRED);
588);
589
590/// A specialized Result type for Poem.
591pub type Result<T, E = Error> = ::std::result::Result<T, E>;
592
593/// Represents a type that can be converted to `poem::Result<T>`.
594///
595/// # Example
596///
597/// ```
598/// use poem::error::{IntoResult, NotFoundError};
599///
600/// let res = "abc".into_result();
601/// assert!(matches!(res, Ok("abc")));
602///
603/// let res = Err::<(), _>(NotFoundError).into_result();
604/// assert!(res.is_err());
605/// let err = res.unwrap_err();
606/// assert!(err.is::<NotFoundError>());
607/// ```
608pub trait IntoResult<T: IntoResponse> {
609    /// Consumes this value returns a `poem::Result<T>`.
610    fn into_result(self) -> Result<T>;
611}
612
613impl<T, E> IntoResult<T> for Result<T, E>
614where
615    T: IntoResponse,
616    E: Into<Error> + Send + Sync + 'static,
617{
618    #[inline]
619    fn into_result(self) -> Result<T> {
620        self.map_err(Into::into)
621    }
622}
623
624impl<T: IntoResponse> IntoResult<T> for T {
625    #[inline]
626    fn into_result(self) -> Result<T> {
627        Ok(self)
628    }
629}
630
631macro_rules! define_simple_errors {
632    ($($(#[$docs:meta])* ($name:ident, $status:ident, $err_msg:literal);)*) => {
633        $(
634        $(#[$docs])*
635        #[derive(Debug, thiserror::Error, Copy, Clone, Eq, PartialEq)]
636        #[error($err_msg)]
637        pub struct $name;
638
639        impl ResponseError for $name {
640            fn status(&self) -> StatusCode {
641                StatusCode::$status
642            }
643        }
644        )*
645    };
646}
647
648define_simple_errors!(
649    /// Only the endpoints under the router can get the path parameters, otherwise this error will occur.
650    (ParsePathError, BAD_REQUEST, "invalid path params");
651
652    /// Error occurred in the router.
653    (NotFoundError, NOT_FOUND, "not found");
654
655    /// Error occurred in the router.
656    (MethodNotAllowedError, METHOD_NOT_ALLOWED, "method not allowed");
657);
658
659/// A possible error value when reading the body.
660#[derive(Debug, thiserror::Error)]
661pub enum ReadBodyError {
662    /// Body has been taken by other extractors.
663    #[error("the body has been taken")]
664    BodyHasBeenTaken,
665
666    /// Body is not a valid utf8 string.
667    #[error("parse utf8: {0}")]
668    Utf8(#[from] FromUtf8Error),
669
670    /// Payload too large
671    #[error("payload too large")]
672    PayloadTooLarge,
673
674    /// Io error.
675    #[error("io: {0}")]
676    Io(#[from] std::io::Error),
677}
678
679impl ResponseError for ReadBodyError {
680    fn status(&self) -> StatusCode {
681        match self {
682            ReadBodyError::BodyHasBeenTaken => StatusCode::INTERNAL_SERVER_ERROR,
683            ReadBodyError::Utf8(_) => StatusCode::BAD_REQUEST,
684            ReadBodyError::Io(_) => StatusCode::BAD_REQUEST,
685            ReadBodyError::PayloadTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
686        }
687    }
688}
689
690/// A possible error value when parsing cookie.
691#[cfg(feature = "cookie")]
692#[cfg_attr(docsrs, doc(cfg(feature = "cookie")))]
693#[derive(Debug, thiserror::Error)]
694pub enum ParseCookieError {
695    /// Cookie value is illegal.
696    #[error("cookie is illegal")]
697    CookieIllegal,
698
699    /// A `Cookie` header is required.
700    #[error("`Cookie` header is required")]
701    CookieHeaderRequired,
702
703    /// Cookie value is illegal.
704    #[error("cookie is illegal: {0}")]
705    #[cfg(not(feature = "sonic-rs"))]
706    ParseJsonValue(#[from] serde_json::Error),
707
708    /// Cookie value is illegal.
709    #[error("cookie is illegal: {0}")]
710    #[cfg(feature = "sonic-rs")]
711    ParseJsonValue(#[from] sonic_rs::Error),
712}
713
714#[cfg(feature = "cookie")]
715impl ResponseError for ParseCookieError {
716    fn status(&self) -> StatusCode {
717        StatusCode::BAD_REQUEST
718    }
719}
720
721/// A possible error value when extracts data from request fails.
722#[derive(Debug, thiserror::Error, Eq, PartialEq)]
723#[error("data of type `{0}` was not found.")]
724pub struct GetDataError(pub &'static str);
725
726impl ResponseError for GetDataError {
727    fn status(&self) -> StatusCode {
728        StatusCode::INTERNAL_SERVER_ERROR
729    }
730}
731
732/// A possible error value when parsing form.
733#[derive(Debug, thiserror::Error)]
734pub enum ParseFormError {
735    /// Invalid content type.
736    #[error("invalid content type `{0}`, expect: `application/x-www-form-urlencoded`")]
737    InvalidContentType(String),
738
739    /// `Content-Type` header is required.
740    #[error("expect content type `application/x-www-form-urlencoded`")]
741    ContentTypeRequired,
742
743    /// Url decode error.
744    #[error("url decode: {0}")]
745    UrlDecode(#[from] serde_urlencoded::de::Error),
746}
747
748impl ResponseError for ParseFormError {
749    fn status(&self) -> StatusCode {
750        match self {
751            ParseFormError::InvalidContentType(_) => StatusCode::UNSUPPORTED_MEDIA_TYPE,
752            ParseFormError::ContentTypeRequired => StatusCode::UNSUPPORTED_MEDIA_TYPE,
753            ParseFormError::UrlDecode(_) => StatusCode::BAD_REQUEST,
754        }
755    }
756}
757
758/// A possible error value when parsing JSON.
759#[derive(Debug, thiserror::Error)]
760pub enum ParseJsonError {
761    /// Invalid content type.
762    #[error("invalid content type `{0}`, expect: `application/json`")]
763    InvalidContentType(String),
764
765    /// `Content-Type` header is required.
766    #[error("expect content type `application/json`")]
767    ContentTypeRequired,
768
769    /// Url decode error.
770    #[error("parse error: {0}")]
771    #[cfg(not(feature = "sonic-rs"))]
772    Parse(#[from] serde_json::Error),
773
774    /// Url decode error.
775    #[error("parse error: {0}")]
776    #[cfg(feature = "sonic-rs")]
777    Parse(#[from] sonic_rs::Error),
778}
779
780impl ResponseError for ParseJsonError {
781    fn status(&self) -> StatusCode {
782        match self {
783            ParseJsonError::InvalidContentType(_) => StatusCode::UNSUPPORTED_MEDIA_TYPE,
784            ParseJsonError::ContentTypeRequired => StatusCode::UNSUPPORTED_MEDIA_TYPE,
785            ParseJsonError::Parse(_) => StatusCode::BAD_REQUEST,
786        }
787    }
788}
789
790/// A possible error value when parsing XML.
791#[cfg(feature = "xml")]
792#[derive(Debug, thiserror::Error)]
793pub enum ParseXmlError {
794    /// Invalid content type.
795    #[error("invalid content type `{0}`, expect: `application/xml`")]
796    InvalidContentType(String),
797
798    /// `Content-Type` header is required.
799    #[error("expect content type `application/xml`")]
800    ContentTypeRequired,
801
802    /// Url decode error.
803    #[error("parse error: {0}")]
804    Parse(#[from] quick_xml::de::DeError),
805}
806
807#[cfg(feature = "xml")]
808impl ResponseError for ParseXmlError {
809    fn status(&self) -> StatusCode {
810        match self {
811            ParseXmlError::InvalidContentType(_) => StatusCode::UNSUPPORTED_MEDIA_TYPE,
812            ParseXmlError::ContentTypeRequired => StatusCode::UNSUPPORTED_MEDIA_TYPE,
813            ParseXmlError::Parse(_) => StatusCode::BAD_REQUEST,
814        }
815    }
816}
817
818/// A possible error value when parsing YAML.
819#[cfg(feature = "yaml")]
820#[derive(Debug, thiserror::Error)]
821pub enum ParseYamlError {
822    /// Invalid content type.
823    #[error("invalid content type `{0}`, expect: `application/yaml`")]
824    InvalidContentType(String),
825
826    /// `Content-Type` header is required.
827    #[error("expect content type `application/yaml`")]
828    ContentTypeRequired,
829
830    /// Url decode error.
831    #[error("parse error: {0}")]
832    Parse(#[from] serde_yaml::Error),
833}
834
835#[cfg(feature = "yaml")]
836impl ResponseError for ParseYamlError {
837    fn status(&self) -> StatusCode {
838        match self {
839            ParseYamlError::InvalidContentType(_) => StatusCode::UNSUPPORTED_MEDIA_TYPE,
840            ParseYamlError::ContentTypeRequired => StatusCode::UNSUPPORTED_MEDIA_TYPE,
841            ParseYamlError::Parse(_) => StatusCode::BAD_REQUEST,
842        }
843    }
844}
845
846/// A possible error value when parsing query.
847#[derive(Debug, thiserror::Error)]
848#[error(transparent)]
849pub struct ParseQueryError(#[from] pub serde_urlencoded::de::Error);
850
851impl ResponseError for ParseQueryError {
852    fn status(&self) -> StatusCode {
853        StatusCode::BAD_REQUEST
854    }
855}
856
857/// A possible error value when parsing multipart.
858#[cfg(feature = "multipart")]
859#[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
860#[derive(Debug, thiserror::Error)]
861pub enum ParseMultipartError {
862    /// Invalid content type.
863    #[error("invalid content type `{0}`, expect: `multipart/form-data`")]
864    InvalidContentType(String),
865
866    /// `Content-Type` header is required.
867    #[error("expect content type `multipart/form-data`")]
868    ContentTypeRequired,
869
870    /// Parse error.
871    #[error("parse: {0}")]
872    Multipart(#[from] multer::Error),
873
874    /// Body is not a valid utf8 string.
875    #[error("parse utf8: {0}")]
876    Utf8(#[from] FromUtf8Error),
877
878    /// Io error
879    #[error("io: {0}")]
880    Io(#[from] std::io::Error),
881}
882
883#[cfg(feature = "multipart")]
884impl ResponseError for ParseMultipartError {
885    fn status(&self) -> StatusCode {
886        match self {
887            ParseMultipartError::InvalidContentType(_) => StatusCode::UNSUPPORTED_MEDIA_TYPE,
888            ParseMultipartError::ContentTypeRequired => StatusCode::UNSUPPORTED_MEDIA_TYPE,
889            ParseMultipartError::Multipart(_) => StatusCode::BAD_REQUEST,
890            ParseMultipartError::Utf8(_) => StatusCode::BAD_REQUEST,
891            ParseMultipartError::Io(_) => StatusCode::BAD_REQUEST,
892        }
893    }
894}
895
896/// A possible error value when parsing typed headers.
897#[derive(Debug, thiserror::Error)]
898pub enum ParseTypedHeaderError {
899    /// A specified header is required.
900    #[error("header `{0}` is required")]
901    HeaderRequired(String),
902
903    /// Parse error.
904    #[error("parse: {0}")]
905    TypedHeader(#[from] headers::Error),
906}
907
908impl ResponseError for ParseTypedHeaderError {
909    fn status(&self) -> StatusCode {
910        StatusCode::BAD_REQUEST
911    }
912}
913
914/// A possible error value when handling websocket.
915#[cfg(feature = "websocket")]
916#[cfg_attr(docsrs, doc(cfg(feature = "websocket")))]
917#[derive(Debug, thiserror::Error)]
918pub enum WebSocketError {
919    /// Invalid protocol
920    #[error("invalid protocol")]
921    InvalidProtocol,
922
923    /// Upgrade Error
924    #[error(transparent)]
925    UpgradeError(#[from] UpgradeError),
926}
927
928#[cfg(feature = "websocket")]
929impl ResponseError for WebSocketError {
930    fn status(&self) -> StatusCode {
931        match self {
932            WebSocketError::InvalidProtocol => StatusCode::BAD_REQUEST,
933            WebSocketError::UpgradeError(err) => err.status(),
934        }
935    }
936}
937
938/// A possible error value when upgrading connection.
939#[derive(Debug, thiserror::Error)]
940pub enum UpgradeError {
941    /// No upgrade
942    #[error("no upgrade")]
943    NoUpgrade,
944
945    /// Other error
946    #[error("{0}")]
947    Other(String),
948}
949
950impl ResponseError for UpgradeError {
951    fn status(&self) -> StatusCode {
952        match self {
953            UpgradeError::NoUpgrade => StatusCode::INTERNAL_SERVER_ERROR,
954            UpgradeError::Other(_) => StatusCode::BAD_REQUEST,
955        }
956    }
957}
958
959/// A possible error value when processing static files.
960#[derive(Debug, thiserror::Error)]
961pub enum StaticFileError {
962    /// Method not allow
963    #[error("method not found")]
964    MethodNotAllowed(Method),
965
966    /// Invalid path
967    #[error("invalid path")]
968    InvalidPath,
969
970    /// Forbidden
971    #[error("forbidden: {0}")]
972    Forbidden(String),
973
974    /// File not found
975    #[error("not found")]
976    NotFound,
977
978    /// Precondition failed
979    #[error("precondition failed")]
980    PreconditionFailed,
981
982    /// Range not satisfiable
983    #[error("range not satisfiable")]
984    RangeNotSatisfiable {
985        /// Content length
986        size: u64,
987    },
988
989    /// Io error
990    #[error("io: {0}")]
991    Io(#[from] std::io::Error),
992}
993
994impl ResponseError for StaticFileError {
995    fn status(&self) -> StatusCode {
996        match self {
997            StaticFileError::MethodNotAllowed(_) => StatusCode::METHOD_NOT_ALLOWED,
998            StaticFileError::InvalidPath => StatusCode::BAD_REQUEST,
999            StaticFileError::Forbidden(_) => StatusCode::FORBIDDEN,
1000            StaticFileError::NotFound => StatusCode::NOT_FOUND,
1001            StaticFileError::PreconditionFailed => StatusCode::PRECONDITION_FAILED,
1002            StaticFileError::RangeNotSatisfiable { .. } => StatusCode::RANGE_NOT_SATISFIABLE,
1003            StaticFileError::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
1004        }
1005    }
1006
1007    fn as_response(&self) -> Response {
1008        let mut resp = self.to_string().into_response();
1009        resp.set_status(self.status());
1010        if let StaticFileError::RangeNotSatisfiable { size } = self {
1011            resp.headers_mut()
1012                .typed_insert(ContentRange::unsatisfied_bytes(*size));
1013        }
1014        resp
1015    }
1016}
1017
1018/// A possible error value occurred in the `SizeLimit` middleware.
1019#[derive(Debug, thiserror::Error, Eq, PartialEq)]
1020pub enum SizedLimitError {
1021    /// Missing `Content-Length` header
1022    #[error("missing `Content-Length` header")]
1023    MissingContentLength,
1024
1025    /// Payload too large
1026    #[error("payload too large")]
1027    PayloadTooLarge,
1028}
1029
1030impl ResponseError for SizedLimitError {
1031    fn status(&self) -> StatusCode {
1032        match self {
1033            SizedLimitError::MissingContentLength => StatusCode::LENGTH_REQUIRED,
1034            SizedLimitError::PayloadTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
1035        }
1036    }
1037}
1038
1039/// A possible error value occurred when adding a route.
1040#[derive(Debug, thiserror::Error, Eq, PartialEq)]
1041pub enum RouteError {
1042    /// Invalid path
1043    #[error("invalid path: {0}")]
1044    InvalidPath(String),
1045
1046    /// Duplicate path
1047    #[error("duplicate path: {0}")]
1048    Duplicate(String),
1049
1050    /// Invalid regex in path
1051    #[error("invalid regex in path: {path}")]
1052    InvalidRegex {
1053        /// Path
1054        path: String,
1055
1056        /// Regex
1057        regex: String,
1058    },
1059}
1060
1061impl ResponseError for RouteError {
1062    fn status(&self) -> StatusCode {
1063        StatusCode::INTERNAL_SERVER_ERROR
1064    }
1065}
1066
1067/// A possible error value occurred in the `Cors` middleware.
1068#[derive(Debug, thiserror::Error, Eq, PartialEq)]
1069pub enum CorsError {
1070    /// Method not allowed
1071    #[error("request-method not allowed")]
1072    MethodNotAllowed,
1073
1074    /// Origin not allowed
1075    #[error("request-origin not allowed")]
1076    OriginNotAllowed,
1077
1078    /// Headers not allowed
1079    #[error("request-headers not allowed")]
1080    HeadersNotAllowed,
1081}
1082
1083impl ResponseError for CorsError {
1084    fn status(&self) -> StatusCode {
1085        StatusCode::FORBIDDEN
1086    }
1087}
1088
1089/// A possible error value occurred when loading i18n resources.
1090#[cfg(feature = "i18n")]
1091#[derive(Debug, thiserror::Error)]
1092pub enum I18NError {
1093    /// Fluent error.
1094    #[error("fluent: {}", .0[0])]
1095    Fluent(Vec<fluent::FluentError>),
1096
1097    /// Fluent FTL parser error.
1098    #[error("fluent parser: {}", .0[0])]
1099    FluentParser(Vec<fluent_syntax::parser::ParserError>),
1100
1101    /// There is no value in the message.
1102    #[error("no value")]
1103    FluentNoValue,
1104
1105    /// Message id was not found.
1106    #[error("msg not found: `{id}`")]
1107    FluentMessageNotFound {
1108        /// Message id
1109        id: String,
1110    },
1111
1112    /// Invalid language id.
1113    #[error("invalid language id: {0}")]
1114    LanguageIdentifier(#[from] unic_langid::LanguageIdentifierError),
1115
1116    /// Io error
1117    #[error("io: {0}")]
1118    Io(#[from] std::io::Error),
1119}
1120
1121#[cfg(feature = "i18n")]
1122impl ResponseError for I18NError {
1123    fn status(&self) -> StatusCode {
1124        StatusCode::INTERNAL_SERVER_ERROR
1125    }
1126}
1127
1128/// A possible error value occurred when deal with redis session.
1129#[cfg(feature = "redis-session")]
1130#[derive(Debug, thiserror::Error)]
1131pub enum RedisSessionError {
1132    /// Redis error.
1133    #[error("redis: {0}")]
1134    Redis(redis::RedisError),
1135}
1136
1137#[cfg(feature = "redis-session")]
1138impl ResponseError for RedisSessionError {
1139    fn status(&self) -> StatusCode {
1140        StatusCode::INTERNAL_SERVER_ERROR
1141    }
1142}
1143
1144#[cfg(test)]
1145mod tests {
1146    use std::io::{Error as IoError, ErrorKind};
1147
1148    use super::*;
1149
1150    #[test]
1151    fn test_into_result() {
1152        assert!(matches!("hello".into_result(), Ok("hello")));
1153        assert!(matches!(Ok::<_, Error>("hello").into_result(), Ok("hello")));
1154        assert!(matches!(
1155            Ok::<_, NotFoundError>("hello").into_result(),
1156            Ok("hello")
1157        ));
1158        assert!(Err::<String, _>(NotFoundError)
1159            .into_result()
1160            .unwrap_err()
1161            .is::<NotFoundError>());
1162    }
1163
1164    #[test]
1165    fn test_error() {
1166        let err = Error::new(
1167            IoError::new(ErrorKind::AlreadyExists, "aaa"),
1168            StatusCode::BAD_GATEWAY,
1169        );
1170        assert!(err.is::<IoError>());
1171        assert_eq!(
1172            err.downcast_ref::<IoError>().unwrap().kind(),
1173            ErrorKind::AlreadyExists
1174        );
1175        assert_eq!(err.into_response().status(), StatusCode::BAD_GATEWAY);
1176    }
1177
1178    #[test]
1179    fn test_box_error() {
1180        let boxed_err: Box<dyn StdError + Send + Sync> =
1181            Box::new(IoError::new(ErrorKind::AlreadyExists, "aaa"));
1182        let err: Error = Error::from((StatusCode::BAD_GATEWAY, boxed_err));
1183        assert!(err.is::<IoError>());
1184        assert_eq!(
1185            err.downcast_ref::<IoError>().unwrap().kind(),
1186            ErrorKind::AlreadyExists
1187        );
1188        assert_eq!(err.into_response().status(), StatusCode::BAD_GATEWAY);
1189    }
1190
1191    #[cfg(feature = "anyhow")]
1192    #[test]
1193    fn test_anyhow_error() {
1194        let anyhow_err: anyhow::Error = IoError::new(ErrorKind::AlreadyExists, "aaa").into();
1195        let err: Error = Error::from((StatusCode::BAD_GATEWAY, anyhow_err));
1196        assert!(err.is::<IoError>());
1197        assert_eq!(
1198            err.downcast_ref::<IoError>().unwrap().kind(),
1199            ErrorKind::AlreadyExists
1200        );
1201        assert_eq!(err.into_response().status(), StatusCode::BAD_GATEWAY);
1202    }
1203
1204    #[cfg(feature = "eyre06")]
1205    #[test]
1206    fn test_eyre06_error() {
1207        let eyre06_err: eyre06::Error = IoError::new(ErrorKind::AlreadyExists, "aaa").into();
1208        let err: Error = Error::from((StatusCode::BAD_GATEWAY, eyre06_err));
1209        assert!(err.is::<IoError>());
1210        assert_eq!(
1211            err.downcast_ref::<IoError>().unwrap().kind(),
1212            ErrorKind::AlreadyExists
1213        );
1214        assert_eq!(err.into_response().status(), StatusCode::BAD_GATEWAY);
1215    }
1216
1217    #[tokio::test]
1218    async fn test_custom_as_response() {
1219        #[derive(Debug, thiserror::Error)]
1220        #[error("my error")]
1221        struct MyError;
1222
1223        impl ResponseError for MyError {
1224            fn status(&self) -> StatusCode {
1225                StatusCode::BAD_GATEWAY
1226            }
1227
1228            fn as_response(&self) -> Response {
1229                Response::builder()
1230                    .status(self.status())
1231                    .body("my error message")
1232            }
1233        }
1234
1235        let err = Error::from(MyError);
1236        let resp = err.into_response();
1237
1238        assert_eq!(resp.status(), StatusCode::BAD_GATEWAY);
1239        assert_eq!(
1240            resp.into_body().into_string().await.unwrap(),
1241            "my error message"
1242        );
1243    }
1244}