1use axum::http::StatusCode;
2use axum::response::{IntoResponse, Response};
3use serde_json::json;
4
5#[derive(Debug)]
7pub enum ApiError {
8 Awa(awa_model::AwaError),
9 ReadOnly,
10 Unauthorized(String),
11}
12
13impl From<awa_model::AwaError> for ApiError {
14 fn from(err: awa_model::AwaError) -> Self {
15 ApiError::Awa(err)
16 }
17}
18
19impl From<sqlx::Error> for ApiError {
20 fn from(err: sqlx::Error) -> Self {
21 ApiError::Awa(err.into())
22 }
23}
24
25impl From<std::sync::Arc<crate::cache::CacheError>> for ApiError {
26 fn from(err: std::sync::Arc<crate::cache::CacheError>) -> Self {
27 let cache_err = std::sync::Arc::try_unwrap(err).unwrap_or_else(|arc| (*arc).clone());
31 ApiError::Awa(
32 std::sync::Arc::try_unwrap(cache_err.0).unwrap_or_else(|arc| {
33 awa_model::AwaError::Database(sqlx::Error::Protocol(arc.to_string()))
34 }),
35 )
36 }
37}
38
39impl IntoResponse for ApiError {
40 fn into_response(self) -> Response {
41 let (status, message) = match self {
42 ApiError::ReadOnly => (
43 StatusCode::SERVICE_UNAVAILABLE,
44 "awa serve is connected to a read-only database; admin actions are disabled"
45 .to_string(),
46 ),
47 ApiError::Unauthorized(message) => (StatusCode::UNAUTHORIZED, message),
48 ApiError::Awa(err) => match &err {
49 awa_model::AwaError::JobNotFound { .. } => (StatusCode::NOT_FOUND, err.to_string()),
50 awa_model::AwaError::CallbackNotFound { .. } => {
51 (StatusCode::NOT_FOUND, err.to_string())
52 }
53 awa_model::AwaError::Validation(_) => (StatusCode::BAD_REQUEST, err.to_string()),
54 awa_model::AwaError::UniqueConflict { .. } => {
55 (StatusCode::CONFLICT, err.to_string())
56 }
57 awa_model::AwaError::Database(_) | awa_model::AwaError::Serialization(_) => {
58 tracing::error!(error = %err, "internal error");
59 (
60 StatusCode::INTERNAL_SERVER_ERROR,
61 "internal server error".to_string(),
62 )
63 }
64 _ => {
65 tracing::error!(error = %err, "internal error");
66 (
67 StatusCode::INTERNAL_SERVER_ERROR,
68 "internal server error".to_string(),
69 )
70 }
71 },
72 };
73
74 let body = axum::Json(json!({ "error": message }));
75 (status, body).into_response()
76 }
77}
78
79impl ApiError {
80 pub fn read_only() -> Self {
81 ApiError::ReadOnly
82 }
83
84 pub fn unauthorized(message: impl Into<String>) -> Self {
85 ApiError::Unauthorized(message.into())
86 }
87}