use axum::{ http::StatusCode, response::{IntoResponse, Response}, Json, }; use serde::Serialize; #[derive(Debug)] pub struct ApiError { pub status: StatusCode, pub message: String, } #[derive(Serialize)] struct ErrorBody<'a> { error: &'a str, } impl ApiError { pub fn bad_request(message: impl Into) -> Self { Self { status: StatusCode::BAD_REQUEST, message: message.into(), } } pub fn unauthorized(message: impl Into) -> Self { Self { status: StatusCode::UNAUTHORIZED, message: message.into(), } } pub fn forbidden(message: impl Into) -> Self { Self { status: StatusCode::FORBIDDEN, message: message.into(), } } pub fn unprocessable_entity(message: impl Into) -> Self { Self { status: StatusCode::UNPROCESSABLE_ENTITY, message: message.into(), } } pub fn not_found(message: impl Into) -> Self { Self { status: StatusCode::NOT_FOUND, message: message.into(), } } pub fn internal(message: impl Into) -> Self { Self { status: StatusCode::INTERNAL_SERVER_ERROR, message: message.into(), } } } impl IntoResponse for ApiError { fn into_response(self) -> Response { ( self.status, Json(ErrorBody { error: &self.message, }), ) .into_response() } } impl From for ApiError { fn from(err: sqlx::Error) -> Self { Self::internal(format!("database error: {err}")) } } impl From for ApiError { fn from(err: std::io::Error) -> Self { Self::internal(format!("IO error: {err}")) } } impl From for ApiError { fn from(err: reqwest::Error) -> Self { Self::internal(format!("HTTP client error: {err}")) } }