Spaces:
Paused
Paused
File size: 2,800 Bytes
71217a7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde::Serialize;
use serde_json::json;
#[derive(Debug, Serialize)]
#[allow(dead_code)]
pub struct ErrorPayload {
pub status: String,
pub code: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub details: Option<serde_json::Value>,
}
pub fn error_payload(
message: &str,
code: &str,
details: Option<serde_json::Value>,
) -> serde_json::Value {
let mut payload = json!({
"status": "error",
"code": code,
"message": message,
});
if let Some(d) = details {
payload["details"] = d;
}
payload
}
pub struct AppError {
pub status_code: StatusCode,
pub body: serde_json::Value,
}
impl AppError {
pub fn new(status_code: StatusCode, message: &str, code: &str) -> Self {
Self {
status_code,
body: json!({ "detail": error_payload(message, code, None) }),
}
}
#[allow(dead_code)]
pub fn with_details(
status_code: StatusCode,
message: &str,
code: &str,
details: serde_json::Value,
) -> Self {
Self {
status_code,
body: json!({ "detail": error_payload(message, code, Some(details)) }),
}
}
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
(self.status_code, axum::Json(self.body)).into_response()
}
}
pub fn http_error(status_code: StatusCode, message: &str, code: &str) -> AppError {
AppError::new(status_code, message, code)
}
// --- Typed error handling ---
#[derive(Debug, thiserror::Error)]
pub enum AppErrorKind {
#[error("Database error: {0}")]
Database(#[from] rusqlite::Error),
#[error("Pool error: {0}")]
Pool(#[from] r2d2::Error),
#[error("Telegram API error: {0}")]
Telegram(String),
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("Configuration error: {0}")]
#[allow(dead_code)]
Config(String),
#[error("{0}")]
Other(String),
}
impl From<AppErrorKind> for AppError {
fn from(kind: AppErrorKind) -> Self {
let (status_code, code) = match &kind {
AppErrorKind::Database(_) | AppErrorKind::Pool(_) => {
(StatusCode::INTERNAL_SERVER_ERROR, "database_error")
}
AppErrorKind::Telegram(_) => (StatusCode::BAD_GATEWAY, "telegram_error"),
AppErrorKind::Http(_) => (StatusCode::BAD_GATEWAY, "http_error"),
AppErrorKind::Config(_) => (StatusCode::SERVICE_UNAVAILABLE, "config_error"),
AppErrorKind::Other(_) => (StatusCode::INTERNAL_SERVER_ERROR, "internal_error"),
};
AppError::new(status_code, &kind.to_string(), code)
}
}
|