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