1use axum::Json;
16use axum::http::{HeaderValue, StatusCode, header};
17use axum::response::{IntoResponse, Response};
18use serde::{Deserialize, Serialize};
19
20#[derive(Debug, Default, Serialize, Deserialize, Clone)]
23struct ErrorData {
24 sub_category: Option<String>,
26 code: Option<String>,
28 exception: Option<bool>,
30 extra: Option<Vec<String>>,
32}
33
34#[derive(Serialize)]
36struct ErrorSerialize<'a> {
37 category: &'a str,
38 message: &'a str,
39 #[serde(flatten)]
40 data: &'a ErrorData,
41}
42
43#[derive(Deserialize)]
45struct ErrorDeserialize {
46 #[serde(default)]
47 category: String,
48 #[serde(default)]
49 message: String,
50 #[serde(flatten)]
51 data: ErrorData,
52}
53
54#[derive(Debug, Clone, Default)]
63pub struct Error {
64 status: u16,
66 category: String,
68 message: String,
70 data: Box<ErrorData>,
72}
73
74impl std::fmt::Display for Error {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 f.write_str(&self.message)
77 }
78}
79
80impl std::error::Error for Error {}
81
82impl Serialize for Error {
84 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
85 where
86 S: serde::Serializer,
87 {
88 ErrorSerialize {
89 category: &self.category,
90 message: &self.message,
91 data: &self.data,
92 }
93 .serialize(serializer)
94 }
95}
96
97impl<'de> Deserialize<'de> for Error {
98 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
99 where
100 D: serde::Deserializer<'de>,
101 {
102 let d = ErrorDeserialize::deserialize(deserializer)?;
103 Ok(Self {
104 status: 0,
105 category: d.category,
106 message: d.message,
107 data: Box::new(d.data),
108 })
109 }
110}
111
112impl Error {
113 #[must_use]
116 pub fn new(message: impl ToString) -> Self {
117 Self {
118 message: message.to_string(),
119 ..Default::default()
120 }
121 }
122
123 #[must_use]
127 pub fn with_category(mut self, category: impl Into<String>) -> Self {
128 self.category = category.into();
129 self
130 }
131
132 #[must_use]
134 pub fn with_sub_category(mut self, sub_category: impl Into<String>) -> Self {
135 self.data.sub_category = Some(sub_category.into());
136 self
137 }
138
139 #[must_use]
141 pub fn with_code(mut self, code: impl Into<String>) -> Self {
142 self.data.code = Some(code.into());
143 self
144 }
145
146 #[must_use]
148 pub fn with_status(mut self, status: u16) -> Self {
149 self.status = status;
150 self
151 }
152
153 #[must_use]
155 pub fn with_exception(mut self, exception: bool) -> Self {
156 self.data.exception = Some(exception);
157 self
158 }
159
160 #[must_use]
162 pub fn add_extra(mut self, value: impl Into<String>) -> Self {
163 self.data
164 .extra
165 .get_or_insert_with(Vec::new)
166 .push(value.into());
167 self
168 }
169
170 pub fn status(&self) -> u16 {
174 self.status
175 }
176
177 pub fn category(&self) -> &str {
179 &self.category
180 }
181
182 pub fn message(&self) -> &str {
184 &self.message
185 }
186
187 pub fn sub_category(&self) -> Option<&str> {
189 self.data.sub_category.as_deref()
190 }
191
192 pub fn code(&self) -> Option<&str> {
194 self.data.code.as_deref()
195 }
196
197 pub fn is_exception(&self) -> bool {
199 self.data.exception.unwrap_or(false)
200 }
201
202 pub fn extra(&self) -> &[String] {
204 self.data.extra.as_deref().unwrap_or(&[])
205 }
206}
207
208impl IntoResponse for Error {
210 fn into_response(self) -> Response {
211 let status = StatusCode::from_u16(self.status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
212 let mut res = if status.is_server_error() || self.is_exception() {
216 let redacted = ErrorData {
217 sub_category: self.data.sub_category.clone(),
218 code: self.data.code.clone(),
219 exception: self.data.exception,
220 extra: None,
221 };
222 (
223 status,
224 Json(ErrorSerialize {
225 category: &self.category,
226 message: "internal server error",
227 data: &redacted,
228 }),
229 )
230 .into_response()
231 } else {
232 (status, Json(&self)).into_response()
233 };
234 res.extensions_mut().insert(self);
236 res.headers_mut()
238 .insert(header::CACHE_CONTROL, HeaderValue::from_static("no-cache"));
239 res
240 }
241}