kellnr_error/
api_error.rs1use 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}