Files
Arkendro/server/src/utils/error.rs

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 }))
}