use axum::{Json, http::StatusCode, response::IntoResponse};
use tracing::warn;
use crate::types::ErrorResponse;
pub type ClResult<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
NotFound,
Gone,
PermissionDenied,
Unauthorized, DbError,
Parse,
ValidationError(String), Conflict(String), PreconditionRequired(String),
SettingNotFound(String),
NetworkError(String), Timeout,
ConfigError(String), ServiceUnavailable(String), Internal(String),
ImageError(String), CryptoError(String),
Io(std::io::Error),
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
warn!("io error: {}", err);
Self::Io(err)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for Error {}
impl IntoResponse for Error {
fn into_response(self) -> axum::response::Response {
let (status, code, message) = match self {
Error::NotFound => (
StatusCode::NOT_FOUND,
"E-CORE-NOTFOUND".to_string(),
"Resource not found".to_string(),
),
Error::Gone => {
(StatusCode::GONE, "E-CORE-GONE".to_string(), "Resource is gone".to_string())
}
Error::PermissionDenied => (
StatusCode::FORBIDDEN,
"E-AUTH-NOPERM".to_string(),
"You do not have permission to access this resource".to_string(),
),
Error::Unauthorized => (
StatusCode::UNAUTHORIZED,
"E-AUTH-UNAUTH".to_string(),
"Authentication required or invalid token".to_string(),
),
Error::ValidationError(msg) => (
StatusCode::BAD_REQUEST,
"E-VAL-INVALID".to_string(),
format!("Request validation failed: {}", msg),
),
Error::Conflict(msg) => (
StatusCode::CONFLICT,
"E-CORE-CONFLICT".to_string(),
format!("Resource conflict: {}", msg),
),
Error::PreconditionRequired(msg) => (
StatusCode::PRECONDITION_REQUIRED,
"E-POW-REQUIRED".to_string(),
format!("Precondition required: {}", msg),
),
Error::SettingNotFound(msg) => (
StatusCode::NOT_FOUND,
"E-SET-NOTFOUND".to_string(),
format!("Setting not configured: {}", msg),
),
Error::Timeout => (
StatusCode::REQUEST_TIMEOUT,
"E-NET-TIMEOUT".to_string(),
"Request timeout".to_string(),
),
Error::ServiceUnavailable(msg) => (
StatusCode::SERVICE_UNAVAILABLE,
"E-SYS-UNAVAIL".to_string(),
format!("Service temporarily unavailable: {}", msg),
),
Error::DbError => (
StatusCode::INTERNAL_SERVER_ERROR,
"E-CORE-DBERR".to_string(),
"Internal server error".to_string(),
),
Error::Internal(msg) => {
warn!("internal error: {}", msg);
(
StatusCode::INTERNAL_SERVER_ERROR,
"E-CORE-INTERNAL".to_string(),
"Internal server error".to_string(),
)
}
Error::Parse => (
StatusCode::INTERNAL_SERVER_ERROR,
"E-CORE-PARSE".to_string(),
"Internal server error".to_string(),
),
Error::Io(_) => (
StatusCode::INTERNAL_SERVER_ERROR,
"E-SYS-IO".to_string(),
"Internal server error".to_string(),
),
Error::NetworkError(_) => (
StatusCode::INTERNAL_SERVER_ERROR,
"E-NET-ERROR".to_string(),
"Internal server error".to_string(),
),
Error::ImageError(_) => (
StatusCode::INTERNAL_SERVER_ERROR,
"E-IMG-PROCFAIL".to_string(),
"Internal server error".to_string(),
),
Error::CryptoError(_) => (
StatusCode::INTERNAL_SERVER_ERROR,
"E-CRYPT-FAIL".to_string(),
"Internal server error".to_string(),
),
Error::ConfigError(_) => (
StatusCode::INTERNAL_SERVER_ERROR,
"E-CONF-CFGERR".to_string(),
"Internal server error".to_string(),
),
};
let error_response = ErrorResponse::new(code, message);
(status, Json(error_response)).into_response()
}
}
impl From<std::num::ParseIntError> for Error {
fn from(err: std::num::ParseIntError) -> Self {
warn!("parse int error: {}", err);
Error::Parse
}
}
impl From<std::time::SystemTimeError> for Error {
fn from(err: std::time::SystemTimeError) -> Self {
warn!("system time error: {}", err);
Error::ServiceUnavailable("system time error".into())
}
}
impl From<axum::Error> for Error {
fn from(err: axum::Error) -> Self {
warn!("axum error: {}", err);
Error::NetworkError("axum error".into())
}
}
impl From<axum::http::Error> for Error {
fn from(err: axum::http::Error) -> Self {
warn!("http error: {}", err);
Error::NetworkError("http error".into())
}
}
impl From<axum::http::header::ToStrError> for Error {
fn from(err: axum::http::header::ToStrError) -> Self {
warn!("header to str error: {}", err);
Error::Parse
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
warn!("json error: {}", err);
Error::Parse
}
}
impl From<tokio::task::JoinError> for Error {
fn from(err: tokio::task::JoinError) -> Self {
warn!("tokio join error: {}", err);
Error::ServiceUnavailable("task execution failed".into())
}
}
#[cfg(feature = "server")]
impl From<instant_acme::Error> for Error {
fn from(err: instant_acme::Error) -> Self {
warn!("acme error: {}", err);
Error::ConfigError("ACME certificate error".into())
}
}
#[cfg(feature = "server")]
impl From<pem::PemError> for Error {
fn from(err: pem::PemError) -> Self {
warn!("pem error: {}", err);
Error::CryptoError("PEM parsing error".into())
}
}
#[cfg(feature = "server")]
impl From<jsonwebtoken::errors::Error> for Error {
fn from(err: jsonwebtoken::errors::Error) -> Self {
warn!("jwt error: {}", err);
Error::Unauthorized
}
}
#[cfg(feature = "server")]
impl From<x509_parser::asn1_rs::Err<x509_parser::error::X509Error>> for Error {
fn from(err: x509_parser::asn1_rs::Err<x509_parser::error::X509Error>) -> Self {
warn!("x509 error: {}", err);
Error::CryptoError("X.509 certificate error".into())
}
}
#[cfg(feature = "server")]
impl From<rustls::Error> for Error {
fn from(err: rustls::Error) -> Self {
warn!("rustls error: {}", err);
Error::CryptoError("TLS error".into())
}
}
#[cfg(feature = "server")]
impl From<rustls_pki_types::pem::Error> for Error {
fn from(err: rustls_pki_types::pem::Error) -> Self {
warn!("pem error: {}", err);
Error::CryptoError("PEM parsing error".into())
}
}
#[cfg(feature = "server")]
impl From<hyper::Error> for Error {
fn from(err: hyper::Error) -> Self {
warn!("hyper error: {}", err);
Error::NetworkError("HTTP client error".into())
}
}
#[cfg(feature = "server")]
impl From<hyper_util::client::legacy::Error> for Error {
fn from(err: hyper_util::client::legacy::Error) -> Self {
warn!("hyper error: {}", err);
Error::NetworkError("HTTP client error".into())
}
}
#[cfg(feature = "server")]
impl From<image::error::ImageError> for Error {
fn from(err: image::error::ImageError) -> Self {
warn!("image error: {:?}", err);
Error::ImageError("Image processing failed".into())
}
}
#[macro_export]
macro_rules! lock {
($mutex:expr) => {
$mutex
.lock()
.map_err(|_| $crate::error::Error::Internal("mutex poisoned".into()))
};
($mutex:expr, $context:expr) => {
$mutex
.lock()
.map_err(|_| $crate::error::Error::Internal(format!("mutex poisoned: {}", $context)))
};
}