use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde_json::json;
#[derive(Debug)]
pub enum ApiError {
Awa(awa_model::AwaError),
ReadOnly,
NotFound(String),
Unauthorized(String),
}
impl From<awa_model::AwaError> for ApiError {
fn from(err: awa_model::AwaError) -> Self {
ApiError::Awa(err)
}
}
impl From<sqlx::Error> for ApiError {
fn from(err: sqlx::Error) -> Self {
ApiError::Awa(err.into())
}
}
impl From<std::sync::Arc<crate::cache::CacheError>> for ApiError {
fn from(err: std::sync::Arc<crate::cache::CacheError>) -> Self {
let cache_err = std::sync::Arc::try_unwrap(err).unwrap_or_else(|arc| (*arc).clone());
ApiError::Awa(
std::sync::Arc::try_unwrap(cache_err.0).unwrap_or_else(|arc| {
awa_model::AwaError::Database(sqlx::Error::Protocol(arc.to_string()))
}),
)
}
}
impl IntoResponse for ApiError {
fn into_response(self) -> Response {
let (status, message) = match self {
ApiError::ReadOnly => (
StatusCode::SERVICE_UNAVAILABLE,
"awa serve is connected to a read-only database; admin actions are disabled"
.to_string(),
),
ApiError::NotFound(message) => (StatusCode::NOT_FOUND, message),
ApiError::Unauthorized(message) => (StatusCode::UNAUTHORIZED, message),
ApiError::Awa(err) => match &err {
awa_model::AwaError::JobNotFound { .. } => (StatusCode::NOT_FOUND, err.to_string()),
awa_model::AwaError::CallbackNotFound { .. } => {
(StatusCode::NOT_FOUND, err.to_string())
}
awa_model::AwaError::Validation(_) => (StatusCode::BAD_REQUEST, err.to_string()),
awa_model::AwaError::UniqueConflict { .. } => {
(StatusCode::CONFLICT, err.to_string())
}
awa_model::AwaError::Database(_) | awa_model::AwaError::Serialization(_) => {
tracing::error!(error = %err, "internal error");
(
StatusCode::INTERNAL_SERVER_ERROR,
"internal server error".to_string(),
)
}
_ => {
tracing::error!(error = %err, "internal error");
(
StatusCode::INTERNAL_SERVER_ERROR,
"internal server error".to_string(),
)
}
},
};
let body = axum::Json(json!({ "error": message }));
(status, body).into_response()
}
}
impl ApiError {
pub fn read_only() -> Self {
ApiError::ReadOnly
}
pub fn unauthorized(message: impl Into<String>) -> Self {
ApiError::Unauthorized(message.into())
}
pub fn not_found(message: impl Into<String>) -> Self {
ApiError::NotFound(message.into())
}
}