1use std::fmt::{self, Display};
2
3use axum::{
4 body::Body,
5 http::{Response, StatusCode},
6 response::IntoResponse,
7};
8
9#[macro_export]
10macro_rules! err_from_type {
11 ($error_type:expr) => {
12 Error {
13 error_type: $error_type,
14 file: file!(),
15 line: line!(),
16 column: column!(),
17 additional: None,
18 }
19 };
20 ($error_type:expr, $($args:tt)*) => {
21 Error {
22 error_type: $error_type,
23 file: file!(),
24 line: line!(),
25 column: column!(),
26 additional: Some(format!($($args)*)),
27 }
28 }
29}
30
31#[macro_export]
32macro_rules! err_with_context {
33 ($err:expr) => {
34 Error::from(($err, file!(), line!(), column!(), None))
35 };
36 ($err:expr, $($args:tt)*) => {
37 Error::from((
38 $err,
39 file!(),
40 line!(),
41 column!(),
42 Some(format!($($args)*)),
43 ))
44 };
45}
46
47pub enum ErrorType {
48 AxumError(axum::http::Error),
49 SerdeError(serde_json::Error),
50 Tokio(tokio::io::Error),
51 Jwt(jsonwebtoken::errors::Error),
52 Chrono,
53 IdenticalBlockType,
54 NotFound,
55 InternalRustError,
56 TokenExpired,
57 Unauthorized,
58}
59
60impl Display for ErrorType {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 match self {
63 ErrorType::AxumError(error) => write!(f, "Axum error: {}", error),
64 ErrorType::SerdeError(error) => write!(f, "Serde error: {}", error),
65 ErrorType::Tokio(error) => write!(f, "Tokio error: {}", error),
66 ErrorType::Jwt(error) => write!(f, "JWT error: {}", error),
67 ErrorType::Chrono => write!(f, "Chrono error"),
68 ErrorType::IdenticalBlockType => write!(f, "Blocktypes Identical"),
69 ErrorType::NotFound => write!(f, "Timeblock Not Found"),
70 ErrorType::InternalRustError => write!(f, "Internal Rust error"),
71 ErrorType::TokenExpired => write!(f, "Access Token timed out"),
72 ErrorType::Unauthorized => write!(f, "Unauthorized Access"),
73 }
74 }
75}
76
77pub struct Error {
78 pub error_type: ErrorType,
79 pub file: &'static str,
80 pub line: u32,
81 pub column: u32,
82 pub additional: Option<String>,
83}
84
85impl Display for Error {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 write!(
88 f,
89 "ERROR: {}:{}:{} : {}",
90 self.file, self.line, self.column, self.error_type
91 )?;
92 if let Some(additional) = &self.additional {
93 write!(f, "\n\tAdditional: {}", additional)?;
94 }
95 Ok(())
96 }
97}
98
99impl IntoResponse for Error {
100 #[allow(clippy::unwrap_used)]
101 fn into_response(self) -> Response<Body> {
102 let status_code = StatusCode::INTERNAL_SERVER_ERROR;
103 Response::builder()
104 .status(status_code)
105 .body(Body::from(self.to_string()))
106 .unwrap()
107 }
108}
109
110impl<E> From<(E, &'static str, u32, u32, Option<String>)> for Error
111where
112 E: Into<ErrorType>,
113{
114 fn from(
115 (err, file, line, column, additional): (E, &'static str, u32, u32, Option<String>),
116 ) -> Self {
117 Error {
118 error_type: err.into(),
119 file,
120 line,
121 column,
122 additional,
123 }
124 }
125}
126
127impl From<axum::http::Error> for ErrorType {
129 fn from(err: axum::http::Error) -> Self {
130 ErrorType::AxumError(err)
131 }
132}
133
134impl From<serde_json::Error> for ErrorType {
135 fn from(err: serde_json::Error) -> Self {
136 ErrorType::SerdeError(err)
137 }
138}
139
140impl From<tokio::io::Error> for ErrorType {
141 fn from(err: tokio::io::Error) -> Self {
142 ErrorType::Tokio(err)
143 }
144}
145
146impl From<chrono::OutOfRangeError> for ErrorType {
147 fn from(_err: chrono::OutOfRangeError) -> Self {
148 ErrorType::Chrono
149 }
150}
151
152impl From<chrono::ParseError> for ErrorType {
153 fn from(_err: chrono::ParseError) -> Self {
154 ErrorType::Chrono
155 }
156}
157
158impl From<jsonwebtoken::errors::Error> for ErrorType {
159 fn from(err: jsonwebtoken::errors::Error) -> Self {
160 ErrorType::Jwt(err)
161 }
162}