use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use serde_json::json; #[derive(Debug)] pub enum AppError { BadRequest(String), Unauthorized(String), Forbidden(String), NotFound(String), TooManyRequests(String), Internal(anyhow::Error), } impl AppError { fn status_and_code(&self) -> (StatusCode, &str) { match self { Self::BadRequest(_) => (StatusCode::BAD_REQUEST, "bad_request"), Self::Unauthorized(_) => (StatusCode::UNAUTHORIZED, "unauthorized"), Self::Forbidden(_) => (StatusCode::FORBIDDEN, "forbidden"), Self::NotFound(_) => (StatusCode::NOT_FOUND, "not_found"), Self::TooManyRequests(_) => (StatusCode::TOO_MANY_REQUESTS, "too_many_requests"), Self::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, "internal_error"), } } fn message(&self) -> String { match self { Self::BadRequest(msg) | Self::Unauthorized(msg) | Self::Forbidden(msg) | Self::NotFound(msg) | Self::TooManyRequests(msg) => msg.clone(), Self::Internal(err) => { tracing::error!("internal error: {err:#}"); "Ein interner Fehler ist aufgetreten.".to_string() } } } } impl IntoResponse for AppError { fn into_response(self) -> Response { let (status, code) = self.status_and_code(); let message = self.message(); let body = json!({ "error": code, "message": message, "status": status.as_u16(), }); (status, axum::Json(body)).into_response() } } impl From for AppError { fn from(err: anyhow::Error) -> Self { Self::Internal(err) } } impl From for AppError { fn from(err: sqlx::Error) -> Self { Self::Internal(err.into()) } }