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}