use std::fmt; use axum::{http::StatusCode, response::IntoResponse}; use crate::{db, ErrorExt}; pub type AppResult = Result; #[derive(Debug)] pub enum AppError { Db(db::DbError), IO(std::io::Error), Other(Box), BadRequest, Unauthorized, NotFound, } impl fmt::Display for AppError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Db(_) => write!(f, "database error"), Self::IO(_) => write!(f, "io error"), Self::Other(_) => write!(f, "other error"), Self::BadRequest => write!(f, "bad request"), Self::Unauthorized => write!(f, "unauthorized"), Self::NotFound => write!(f, "not found"), } } } impl std::error::Error for AppError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::Db(err) => Some(err), Self::IO(err) => Some(err), Self::Other(err) => Some(err.as_ref()), Self::NotFound | Self::Unauthorized | Self::BadRequest => None, } } } impl From for AppError { fn from(value: db::DbError) -> Self { Self::Db(value) } } impl From for AppError { fn from(value: std::io::Error) -> Self { Self::IO(value) } } impl IntoResponse for AppError { fn into_response(self) -> axum::response::Response { match self { Self::NotFound => StatusCode::NOT_FOUND.into_response(), Self::Unauthorized => StatusCode::UNAUTHORIZED.into_response(), Self::BadRequest => StatusCode::BAD_REQUEST.into_response(), _ => { tracing::error!("{}", self.stack()); StatusCode::INTERNAL_SERVER_ERROR.into_response() } } } }