1use axum::http::StatusCode;
6use axum::response::{IntoResponse, Response};
7use serde_json::json;
8
9pub enum ApiError {
11 Storage(tuitbot_core::error::StorageError),
13 NotFound(String),
15 BadRequest(String),
17 Conflict(String),
19 Internal(String),
21 Forbidden(String),
23}
24
25impl From<tuitbot_core::error::StorageError> for ApiError {
26 fn from(err: tuitbot_core::error::StorageError) -> Self {
27 Self::Storage(err)
28 }
29}
30
31impl From<crate::account::AccountError> for ApiError {
32 fn from(err: crate::account::AccountError) -> Self {
33 match err.status {
34 StatusCode::FORBIDDEN => Self::Forbidden(err.message),
35 StatusCode::NOT_FOUND => Self::NotFound(err.message),
36 _ => Self::Internal(err.message),
37 }
38 }
39}
40
41impl IntoResponse for ApiError {
42 fn into_response(self) -> Response {
43 let (status, message) = match self {
44 Self::Storage(e) => {
45 tracing::error!("storage error: {e}");
46 (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
47 }
48 Self::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
49 Self::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
50 Self::Conflict(msg) => (StatusCode::CONFLICT, msg),
51 Self::Internal(msg) => {
52 tracing::error!("internal error: {msg}");
53 (StatusCode::INTERNAL_SERVER_ERROR, msg)
54 }
55 Self::Forbidden(msg) => (StatusCode::FORBIDDEN, msg),
56 };
57
58 let body = axum::Json(json!({ "error": message }));
59 (status, body).into_response()
60 }
61}