Sam Jeston
History
62 lines·
2.0 KB
·Rust
| 1 | use axum::http::StatusCode; |
| 2 | use axum::response::{IntoResponse, Redirect, Response}; |
| 3 | |
| 4 | /// Errors returned by page handlers. The context middleware turns these into |
| 5 | /// full HTML error pages. |
| 6 | #[derive(Debug)] |
| 7 | pub enum AppError { |
| 8 | NotFound, |
| 9 | Forbidden(String), |
| 10 | BadRequest(String), |
| 11 | /// Redirects to the sign-in page, returning to the given path afterwards. |
| 12 | LoginRequired(String), |
| 13 | Internal(anyhow::Error), |
| 14 | } |
| 15 | |
| 16 | impl<E: Into<anyhow::Error>> From<E> for AppError { |
| 17 | fn from(e: E) -> Self { |
| 18 | AppError::Internal(e.into()) |
| 19 | } |
| 20 | } |
| 21 | |
| 22 | /// Marker attached to error responses so the middleware can render them. |
| 23 | #[derive(Debug, Clone)] |
| 24 | pub struct ErrorPage { |
| 25 | pub status: StatusCode, |
| 26 | pub title: &'static str, |
| 27 | pub message: String, |
| 28 | } |
| 29 | |
| 30 | impl IntoResponse for AppError { |
| 31 | fn into_response(self) -> Response { |
| 32 | let (status, title, message) = match self { |
| 33 | AppError::LoginRequired(next) => { |
| 34 | let target = format!("/login?next={}", super::fmt::urlencode(&next)); |
| 35 | return Redirect::to(&target).into_response(); |
| 36 | } |
| 37 | AppError::NotFound => ( |
| 38 | StatusCode::NOT_FOUND, |
| 39 | "Page not found", |
| 40 | "The page you are looking for does not exist, or you do not have access to it." |
| 41 | .to_string(), |
| 42 | ), |
| 43 | AppError::Forbidden(msg) => (StatusCode::FORBIDDEN, "Access denied", msg), |
| 44 | AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, "Bad request", msg), |
| 45 | AppError::Internal(err) => { |
| 46 | tracing::error!("internal error: {err:#}"); |
| 47 | ( |
| 48 | StatusCode::INTERNAL_SERVER_ERROR, |
| 49 | "Something went wrong", |
| 50 | "An unexpected error occurred. Please try again.".to_string(), |
| 51 | ) |
| 52 | } |
| 53 | }; |
| 54 | let mut resp = (status, format!("{title}: {message}")).into_response(); |
| 55 | resp.extensions_mut().insert(ErrorPage { |
| 56 | status, |
| 57 | title, |
| 58 | message, |
| 59 | }); |
| 60 | resp |
| 61 | } |
| 62 | } |