use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; #[derive(Debug, thiserror::Error)] pub enum SmartError { #[error("{0}")] BadRequest(String), #[error("unauthorized")] Unauthorized, #[error("{0}")] Forbidden(String), #[error("{0}")] NotFound(String), #[error("{0}")] Conflict(String), #[error("{0}")] Internal(String), #[error("io error: {0}")] Io(#[from] std::io::Error), #[error("database error: {0}")] Db(#[from] sea_orm::DbErr), #[error("redis error: {0}")] Redis(#[from] redis::RedisError), } pub type SmartResult = Result; impl SmartError { pub fn status(&self) -> StatusCode { match self { Self::BadRequest(_) => StatusCode::BAD_REQUEST, Self::Unauthorized => StatusCode::UNAUTHORIZED, Self::Forbidden(_) => StatusCode::FORBIDDEN, Self::NotFound(_) => StatusCode::NOT_FOUND, Self::Conflict(_) => StatusCode::CONFLICT, _ => StatusCode::INTERNAL_SERVER_ERROR, } } pub fn message(&self) -> String { match self { Self::BadRequest(m) | Self::Forbidden(m) | Self::NotFound(m) | Self::Conflict(m) => { m.clone() } Self::Internal(m) => { tracing::warn!(error = %m, "smart http internal error"); "internal server error".to_string() } other => other.to_string(), } } } impl From for SmartError { fn from(err: crate::share::errors::GitError) -> Self { use crate::share::errors::GitError; match err { GitError::Unauthorized => Self::Unauthorized, GitError::NotFound => Self::NotFound("resource not found".into()), GitError::Forbidden => Self::Forbidden("forbidden".into()), GitError::InvalidArgument(m) => Self::BadRequest(m), GitError::Conflict(m) => Self::Conflict(m), other => Self::Internal(other.to_string()), } } } impl IntoResponse for SmartError { fn into_response(self) -> Response { let status = self.status(); let mut resp = (status, self.message()).into_response(); if status == StatusCode::UNAUTHORIZED { resp.headers_mut().insert( axum::http::header::WWW_AUTHENTICATE, axum::http::HeaderValue::from_static("Basic realm=\"gitdata\""), ); } resp } }