Skip to main content

kellnr_error/
api_error.rs

1use std::fmt::Display;
2
3use axum::Json;
4use axum::http::StatusCode;
5use axum::response::{IntoResponse, Response};
6use kellnr_common::original_name::NameError;
7use kellnr_common::version::VersionError;
8use serde::{Deserialize, Serialize};
9use zip::result::ZipError;
10
11pub type ApiResult<T> = Result<T, ApiError>;
12
13pub struct ApiError {
14    status: StatusCode,
15    details: ErrorDetails,
16}
17
18#[derive(Serialize, Deserialize, Debug)]
19pub struct ErrorDetails {
20    pub errors: Vec<ErrorDetail>,
21}
22
23impl From<String> for ErrorDetails {
24    fn from(e: String) -> Self {
25        let detail = ErrorDetail { detail: e };
26        Self {
27            errors: vec![detail],
28        }
29    }
30}
31
32#[derive(Serialize, Deserialize, Debug)]
33pub struct ErrorDetail {
34    pub detail: String,
35}
36
37impl ApiError {
38    pub fn new(msg: &str, error: &str, status: StatusCode) -> Self {
39        let e = if error.is_empty() {
40            format!("ERROR: {msg}")
41        } else {
42            format!("ERROR: {msg} -> {error}")
43        };
44        Self {
45            status,
46            details: ErrorDetails::from(e),
47        }
48    }
49
50    fn from_str(e: &str, status: StatusCode) -> Self {
51        Self {
52            status,
53            details: ErrorDetails::from(format!("ERROR: {e}")),
54        }
55    }
56
57    pub fn from_err(e: &dyn std::error::Error, status: StatusCode) -> Self {
58        let e = format!("ERROR: {e}");
59        Self {
60            status,
61            details: ErrorDetails::from(e),
62        }
63    }
64}
65
66impl IntoResponse for ApiError {
67    fn into_response(self) -> Response {
68        (self.status, Json(self.details)).into_response()
69    }
70}
71
72impl From<NameError> for ApiError {
73    fn from(e: NameError) -> Self {
74        ApiError::from_err(&e, StatusCode::BAD_REQUEST)
75    }
76}
77
78impl From<VersionError> for ApiError {
79    fn from(e: VersionError) -> Self {
80        ApiError::from_err(&e, StatusCode::BAD_REQUEST)
81    }
82}
83
84impl From<std::io::Error> for ApiError {
85    fn from(e: std::io::Error) -> Self {
86        ApiError::from_err(&e, StatusCode::INTERNAL_SERVER_ERROR)
87    }
88}
89
90impl From<ZipError> for ApiError {
91    fn from(e: ZipError) -> Self {
92        match e {
93            ZipError::Io(e) => ApiError::from_err(&e, StatusCode::INTERNAL_SERVER_ERROR),
94            ZipError::InvalidArchive(s) => ApiError::from_str(&s, StatusCode::BAD_REQUEST),
95            ZipError::UnsupportedArchive(s) => ApiError::from_str(s, StatusCode::BAD_REQUEST),
96            ZipError::FileNotFound => {
97                ApiError::from_str("Zip archive not found", StatusCode::NOT_FOUND)
98            }
99            _ => ApiError::from_str("Unknown zip error", StatusCode::INTERNAL_SERVER_ERROR),
100        }
101    }
102}
103
104impl Display for ApiError {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        write!(f, "{}", self.details.errors[0].detail)
107    }
108}
109
110impl From<kellnr_db::error::DbError> for ApiError {
111    fn from(e: kellnr_db::error::DbError) -> Self {
112        ApiError::from_err(&e, StatusCode::INTERNAL_SERVER_ERROR)
113    }
114}
115
116impl From<kellnr_storage::storage_error::StorageError> for ApiError {
117    fn from(e: kellnr_storage::storage_error::StorageError) -> Self {
118        ApiError::from_err(&e, StatusCode::INTERNAL_SERVER_ERROR)
119    }
120}