143 lines
4.6 KiB
Rust
143 lines
4.6 KiB
Rust
use axum::{
|
|
http::StatusCode,
|
|
response::{IntoResponse, Json, Response},
|
|
};
|
|
use serde_json::json;
|
|
use std::fmt;
|
|
|
|
#[derive(Debug)]
|
|
pub enum AppError {
|
|
DatabaseError(String),
|
|
ValidationError(String),
|
|
AuthenticationError(String),
|
|
AuthorizationError(String),
|
|
NotFoundError(String),
|
|
ConflictError(String),
|
|
InternalError(String),
|
|
}
|
|
|
|
impl fmt::Display for AppError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
AppError::DatabaseError(msg) => write!(f, "Database error: {}", msg),
|
|
AppError::ValidationError(msg) => write!(f, "Validation error: {}", msg),
|
|
AppError::AuthenticationError(msg) => write!(f, "Authentication error: {}", msg),
|
|
AppError::AuthorizationError(msg) => write!(f, "Authorization error: {}", msg),
|
|
AppError::NotFoundError(msg) => write!(f, "Not found: {}", msg),
|
|
AppError::ConflictError(msg) => write!(f, "Conflict: {}", msg),
|
|
AppError::InternalError(msg) => write!(f, "Internal error: {}", msg),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for AppError {}
|
|
|
|
impl IntoResponse for AppError {
|
|
fn into_response(self) -> Response {
|
|
let (status, error_message) = match self {
|
|
AppError::DatabaseError(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Database error"),
|
|
AppError::ValidationError(ref msg) => (StatusCode::BAD_REQUEST, msg.as_str()),
|
|
AppError::AuthenticationError(ref msg) => (StatusCode::UNAUTHORIZED, msg.as_str()),
|
|
AppError::AuthorizationError(ref msg) => (StatusCode::FORBIDDEN, msg.as_str()),
|
|
AppError::NotFoundError(ref msg) => (StatusCode::NOT_FOUND, msg.as_str()),
|
|
AppError::ConflictError(ref msg) => (StatusCode::CONFLICT, msg.as_str()),
|
|
AppError::InternalError(_) => {
|
|
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error")
|
|
}
|
|
};
|
|
|
|
let body = Json(json!({
|
|
"error": error_message
|
|
}));
|
|
|
|
(status, body).into_response()
|
|
}
|
|
}
|
|
|
|
impl From<anyhow::Error> for AppError {
|
|
fn from(err: anyhow::Error) -> Self {
|
|
if let Some(sqlx_err) = err.downcast_ref::<sqlx::Error>() {
|
|
match sqlx_err {
|
|
sqlx::Error::RowNotFound => {
|
|
AppError::NotFoundError("Resource not found".to_string())
|
|
}
|
|
sqlx::Error::Database(db_err) => {
|
|
if db_err.message().contains("UNIQUE constraint failed") {
|
|
AppError::ConflictError("Resource already exists".to_string())
|
|
} else {
|
|
AppError::DatabaseError(db_err.message().to_string())
|
|
}
|
|
}
|
|
_ => AppError::DatabaseError("Database operation failed".to_string()),
|
|
}
|
|
} else {
|
|
AppError::InternalError(err.to_string())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<bcrypt::BcryptError> for AppError {
|
|
fn from(_: bcrypt::BcryptError) -> Self {
|
|
AppError::InternalError("Password hashing error".to_string())
|
|
}
|
|
}
|
|
|
|
impl From<sqlx::Error> for AppError {
|
|
fn from(err: sqlx::Error) -> Self {
|
|
match err {
|
|
sqlx::Error::RowNotFound => AppError::NotFoundError("Resource not found".to_string()),
|
|
sqlx::Error::Database(db_err) => {
|
|
if db_err.message().contains("UNIQUE constraint failed") {
|
|
AppError::ConflictError("Resource already exists".to_string())
|
|
} else {
|
|
AppError::DatabaseError(db_err.message().to_string())
|
|
}
|
|
}
|
|
_ => AppError::DatabaseError("Database operation failed".to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<std::io::Error> for AppError {
|
|
fn from(err: std::io::Error) -> Self {
|
|
AppError::InternalError(format!("IO error: {}", err))
|
|
}
|
|
}
|
|
|
|
pub type AppResult<T> = Result<T, AppError>;
|
|
|
|
pub fn validation_error(msg: &str) -> AppError {
|
|
AppError::ValidationError(msg.to_string())
|
|
}
|
|
|
|
pub fn auth_error(msg: &str) -> AppError {
|
|
AppError::AuthenticationError(msg.to_string())
|
|
}
|
|
|
|
pub fn forbidden_error(msg: &str) -> AppError {
|
|
AppError::AuthorizationError(msg.to_string())
|
|
}
|
|
|
|
pub fn not_found_error(msg: &str) -> AppError {
|
|
AppError::NotFoundError(msg.to_string())
|
|
}
|
|
|
|
pub fn conflict_error(msg: &str) -> AppError {
|
|
AppError::ConflictError(msg.to_string())
|
|
}
|
|
|
|
pub fn internal_error(msg: &str) -> AppError {
|
|
AppError::InternalError(msg.to_string())
|
|
}
|
|
|
|
pub fn success_response<T>(data: T) -> Json<T>
|
|
where
|
|
T: serde::Serialize,
|
|
{
|
|
Json(data)
|
|
}
|
|
|
|
pub fn success_message(msg: &str) -> Json<serde_json::Value> {
|
|
Json(json!({ "message": msg }))
|
|
}
|