1use crate::metadata::FormatConfig;
4use crate::registry::{
5 http_headers, http_status, render_graphql, render_html, render_json, render_jsonrpc,
6 render_text,
7};
8
9fn fallback_json(message: impl std::fmt::Display) -> serde_json::Value {
11 serde_json::json!({
12 "error": "UNKNOWN_ERROR",
13 "message": message.to_string()
14 })
15}
16
17fn fallback_html(message: impl std::fmt::Display) -> String {
19 format!(
20 "<div class=\"error\">\n<h3 class=\"error-code\">UNKNOWN_ERROR</h3>\n<p class=\"error-message\">{message}</p>\n</div>"
21 )
22}
23
24fn fallback_graphql(message: impl std::fmt::Display) -> serde_json::Value {
26 serde_json::json!({
27 "message": message.to_string(),
28 "extensions": {
29 "code": "UNKNOWN_ERROR"
30 }
31 })
32}
33
34fn fallback_text(message: impl std::fmt::Display) -> String {
36 format!("[UNKNOWN_ERROR] {message}")
37}
38
39fn fallback_jsonrpc(message: impl std::fmt::Display) -> serde_json::Value {
41 serde_json::json!({
42 "code": crate::private::DEFAULT_JSONRPC_CODE,
43 "message": message.to_string(),
44 "data": {
45 "diagnostic_code": "UNKNOWN_ERROR"
46 }
47 })
48}
49
50pub trait ErrorExt {
68 fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
74
75 fn to_html(&self, config: FormatConfig) -> String;
77
78 fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
84
85 fn to_text(&self, config: FormatConfig) -> String;
87
88 fn to_debug(&self) -> String
90 where
91 Self: std::fmt::Debug,
92 {
93 format!("{self:?}")
94 }
95
96 fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
102
103 fn http_status(&self) -> http::StatusCode;
105
106 fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)>;
108}
109
110impl<E: std::error::Error + 'static> ErrorExt for E {
112 fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
113 render_json(self, config).unwrap_or_else(|| Ok(fallback_json(self)))
114 }
115
116 fn to_html(&self, config: FormatConfig) -> String {
117 render_html(self, config).unwrap_or_else(|| fallback_html(self))
118 }
119
120 fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
121 render_graphql(self, config).unwrap_or_else(|| Ok(fallback_graphql(self)))
122 }
123
124 fn to_text(&self, config: FormatConfig) -> String {
125 render_text(self, config).unwrap_or_else(|| fallback_text(self))
126 }
127
128 fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
129 render_jsonrpc(self, config).unwrap_or_else(|| Ok(fallback_jsonrpc(self)))
130 }
131
132 fn http_status(&self) -> http::StatusCode {
133 http_status(self).unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR)
134 }
135
136 fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)> {
137 http_headers(self).unwrap_or_default()
138 }
139}
140
141pub trait HeapErrorExt {
172 fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
178
179 fn to_html(&self, config: FormatConfig) -> String;
181
182 fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
188
189 fn to_text(&self, config: FormatConfig) -> String;
191
192 fn to_debug(&self) -> String;
194
195 fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
201
202 fn http_status(&self) -> http::StatusCode;
204
205 fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)>;
207}
208
209impl HeapErrorExt for Box<dyn std::error::Error + Send + Sync> {
215 fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
216 let error_ref: &dyn std::error::Error = self.as_ref();
217
218 if let Some(nested_arc) =
220 error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
221 {
222 return HeapErrorExt::to_json(nested_arc, config);
223 }
224
225 render_json(error_ref, config).unwrap_or_else(|| Ok(fallback_json(error_ref)))
226 }
227
228 fn to_html(&self, config: FormatConfig) -> String {
229 let error_ref: &dyn std::error::Error = self.as_ref();
230
231 if let Some(nested_arc) =
233 error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
234 {
235 return HeapErrorExt::to_html(nested_arc, config);
236 }
237
238 render_html(error_ref, config).unwrap_or_else(|| fallback_html(error_ref))
239 }
240
241 fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
242 let error_ref: &dyn std::error::Error = self.as_ref();
243
244 if let Some(nested_arc) =
246 error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
247 {
248 return HeapErrorExt::to_graphql(nested_arc, config);
249 }
250
251 render_graphql(error_ref, config).unwrap_or_else(|| Ok(fallback_graphql(error_ref)))
252 }
253
254 fn to_text(&self, config: FormatConfig) -> String {
255 let error_ref: &dyn std::error::Error = self.as_ref();
256
257 if let Some(nested_arc) =
259 error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
260 {
261 return HeapErrorExt::to_text(nested_arc, config);
262 }
263
264 render_text(error_ref, config).unwrap_or_else(|| fallback_text(error_ref))
265 }
266
267 fn to_debug(&self) -> String {
268 format!("{self:?}")
269 }
270
271 fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
272 let error_ref: &dyn std::error::Error = self.as_ref();
273
274 if let Some(nested_arc) =
276 error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
277 {
278 return HeapErrorExt::to_jsonrpc(nested_arc, config);
279 }
280
281 render_jsonrpc(error_ref, config).unwrap_or_else(|| Ok(fallback_jsonrpc(error_ref)))
282 }
283
284 fn http_status(&self) -> http::StatusCode {
285 let error_ref: &dyn std::error::Error = self.as_ref();
286
287 if let Some(nested_arc) =
289 error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
290 {
291 return HeapErrorExt::http_status(nested_arc);
292 }
293
294 http_status(error_ref).unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR)
295 }
296
297 fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)> {
298 let error_ref: &dyn std::error::Error = self.as_ref();
299
300 if let Some(nested_arc) =
302 error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
303 {
304 return HeapErrorExt::http_headers(nested_arc);
305 }
306
307 http_headers(error_ref).unwrap_or_default()
308 }
309}
310
311impl HeapErrorExt for std::sync::Arc<dyn std::error::Error + Send + Sync> {
317 fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
318 let error_ref: &dyn std::error::Error = self.as_ref();
319 render_json(error_ref, config).unwrap_or_else(|| Ok(fallback_json(error_ref)))
320 }
321
322 fn to_html(&self, config: FormatConfig) -> String {
323 let error_ref: &dyn std::error::Error = self.as_ref();
324 render_html(error_ref, config).unwrap_or_else(|| fallback_html(error_ref))
325 }
326
327 fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
328 let error_ref: &dyn std::error::Error = self.as_ref();
329 render_graphql(error_ref, config).unwrap_or_else(|| Ok(fallback_graphql(error_ref)))
330 }
331
332 fn to_text(&self, config: FormatConfig) -> String {
333 let error_ref: &dyn std::error::Error = self.as_ref();
334 render_text(error_ref, config).unwrap_or_else(|| fallback_text(error_ref))
335 }
336
337 fn to_debug(&self) -> String {
338 format!("{self:?}")
339 }
340
341 fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
342 let error_ref: &dyn std::error::Error = self.as_ref();
343 render_jsonrpc(error_ref, config).unwrap_or_else(|| Ok(fallback_jsonrpc(error_ref)))
344 }
345
346 fn http_status(&self) -> http::StatusCode {
347 let error_ref: &dyn std::error::Error = self.as_ref();
348 http_status(error_ref).unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR)
349 }
350
351 fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)> {
352 let error_ref: &dyn std::error::Error = self.as_ref();
353 http_headers(error_ref).unwrap_or_default()
354 }
355}