1use axum::{
4 http::StatusCode,
5 response::{IntoResponse, Response},
6 Json,
7};
8use serde_json::json;
9use thiserror::Error;
10
11#[derive(Error, Debug)]
12pub enum ApiError {
13 #[error("Unauthorized: {0}")]
14 Unauthorized(String),
15
16 #[error("Forbidden: {0}")]
17 Forbidden(String),
18
19 #[error("Not found: {0}")]
20 NotFound(String),
21
22 #[error("Bad request: {0}")]
23 BadRequest(String),
24
25 #[error("Conflict: {0}")]
26 Conflict(String),
27
28 #[error("Quota exceeded: {0}")]
29 QuotaExceeded(String),
30
31 #[error("Internal server error: {0}")]
32 InternalError(String),
33
34 #[error("Payment error: {0}")]
35 PaymentError(String),
36
37 #[error("Authentication failed: {0}")]
38 AuthenticationFailed(String),
39}
40
41impl IntoResponse for ApiError {
42 fn into_response(self) -> Response {
43 let (status, error_message) = match self {
44 ApiError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg),
45 ApiError::Forbidden(msg) => (StatusCode::FORBIDDEN, msg),
46 ApiError::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
47 ApiError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
48 ApiError::Conflict(msg) => (StatusCode::CONFLICT, msg),
49 ApiError::QuotaExceeded(msg) => (StatusCode::PAYMENT_REQUIRED, msg),
50 ApiError::InternalError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
51 ApiError::PaymentError(msg) => (StatusCode::PAYMENT_REQUIRED, msg),
52 ApiError::AuthenticationFailed(msg) => (StatusCode::UNAUTHORIZED, msg),
53 };
54
55 let body = Json(json!({
56 "error": error_message,
57 "status": status.as_u16(),
58 }));
59
60 (status, body).into_response()
61 }
62}
63
64pub type ApiResult<T> = Result<T, ApiError>;