1use axum::http::StatusCode;
4use axum::response::{IntoResponse, Response};
5use serde_json::json;
6
7#[derive(Debug)]
9#[allow(dead_code)]
10pub enum ApiError {
11 BadRequest(String),
12 NotFound(String),
13 Conflict(String),
14 Unauthorized(String),
15 Forbidden(String),
16 RateLimited,
17 Internal(String),
18 ServiceUnavailable(String),
19}
20
21impl std::fmt::Display for ApiError {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 Self::BadRequest(msg) => write!(f, "bad request: {msg}"),
25 Self::NotFound(msg) => write!(f, "not found: {msg}"),
26 Self::Conflict(msg) => write!(f, "conflict: {msg}"),
27 Self::Unauthorized(msg) => write!(f, "unauthorized: {msg}"),
28 Self::Forbidden(msg) => write!(f, "forbidden: {msg}"),
29 Self::RateLimited => write!(f, "rate limited"),
30 Self::Internal(msg) => write!(f, "internal error: {msg}"),
31 Self::ServiceUnavailable(msg) => write!(f, "service unavailable: {msg}"),
32 }
33 }
34}
35
36impl IntoResponse for ApiError {
37 fn into_response(self) -> Response {
38 let (status, message) = match &self {
39 Self::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
40 Self::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
41 Self::Conflict(msg) => (StatusCode::CONFLICT, msg.clone()),
42 Self::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg.clone()),
43 Self::Forbidden(msg) => (StatusCode::FORBIDDEN, msg.clone()),
44 Self::RateLimited => (
45 StatusCode::TOO_MANY_REQUESTS,
46 "too many requests".to_string(),
47 ),
48 Self::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
49 Self::ServiceUnavailable(msg) => (StatusCode::SERVICE_UNAVAILABLE, msg.clone()),
50 };
51
52 let body = json!({ "error": message });
53 (status, axum::Json(body)).into_response()
54 }
55}