pavex/error/
mod.rs

1//! Error handling utilities.
2pub(crate) mod error_;
3
4/// When things went wrong, but you don't know why.
5///
6/// `UnexpectedError` is designed for failure scenarios
7/// that the application wasn't explicitly prepared to handle.
8/// It works, in particular, as the "catch-all" variant in
9/// an error enum.
10///
11/// # Example
12///
13/// ```rust
14/// use pavex::error::UnexpectedError;
15/// use pavex::Response;
16/// # #[derive(Debug)] struct AuthorizationError;
17/// # #[derive(Debug)] struct DatabaseError;
18/// # impl std::fmt::Display for AuthorizationError {
19/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20/// #         write!(f, "Authorization error")
21/// #     }
22/// # }
23/// # impl std::fmt::Display for DatabaseError {
24/// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25/// #         write!(f, "Database error")
26/// #     }
27/// # }
28/// # impl std::error::Error for AuthorizationError {}
29/// # impl std::error::Error for DatabaseError {}
30///
31/// #[derive(Debug, thiserror::Error)]
32/// pub enum HandlerError {
33///     // One variant for each kind of known issue
34///     // that might occur in the request handler.
35///     #[error(transparent)]
36///     Authorization(#[from] AuthorizationError),
37///     #[error(transparent)]
38///     Database(#[from] DatabaseError),
39///     // [...]
40///     // Followed by the catch-all variant.
41///     #[error(transparent)]
42///     Unexpected(#[from] UnexpectedError),
43/// }
44///
45/// pub async fn request_handler() -> Result<Response, HandlerError> {
46///     // [...]
47/// # todo!()
48/// }
49/// ```
50///
51/// # Error message
52///
53/// The error message is always the same when using `UnexpectedError`:
54/// "An unexpected error occurred".
55/// This is intentional, as we don't want to leak any sensitive information
56/// or implementation details to the client.
57/// The full error details are still available when walking the source error chain and
58/// will be captured in your logs if you have a suitable error observer in place.
59#[derive(Debug)]
60pub struct UnexpectedError {
61    inner: Box<dyn std::error::Error + Send + Sync>,
62}
63
64impl UnexpectedError {
65    /// Create a new [`UnexpectedError`] from a boxable error.
66    pub fn new<E>(error: E) -> Self
67    where
68        E: Into<Box<dyn std::error::Error + Send + Sync>>,
69    {
70        Self {
71            inner: error.into(),
72        }
73    }
74
75    /// Convert [`UnexpectedError`] back into the underlying boxed error.
76    pub fn into_inner(self) -> Box<dyn std::error::Error + Send + Sync> {
77        self.inner
78    }
79
80    /// Return a reference to the underlying boxed error.
81    pub fn inner_ref(&self) -> &(dyn std::error::Error + Send + Sync) {
82        &*self.inner
83    }
84}
85
86impl std::fmt::Display for UnexpectedError {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        write!(f, "An unexpected error occurred")
89    }
90}
91
92impl std::error::Error for UnexpectedError {
93    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
94        Some(&*self.inner)
95    }
96}