mas_axum_utils/
fancy_error.rs1use axum::{
8 Extension,
9 http::StatusCode,
10 response::{IntoResponse, Response},
11};
12use axum_extra::typed_header::TypedHeader;
13use headers::ContentType;
14use mas_templates::ErrorContext;
15
16use crate::sentry::SentryEventID;
17
18fn build_context(mut err: &dyn std::error::Error) -> ErrorContext {
19 let description = err.to_string();
20 let mut details = Vec::new();
21 while let Some(source) = err.source() {
22 err = source;
23 details.push(err.to_string());
24 }
25
26 ErrorContext::new()
27 .with_description(description)
28 .with_details(details.join("\n"))
29}
30
31pub struct GenericError {
32 error: Box<dyn std::error::Error + 'static>,
33 code: StatusCode,
34}
35
36impl IntoResponse for GenericError {
37 fn into_response(self) -> Response {
38 tracing::warn!(message = &*self.error);
39 let context = build_context(&*self.error);
40 let context_text = format!("{context}");
41
42 (
43 self.code,
44 TypedHeader(ContentType::text()),
45 Extension(context),
46 context_text,
47 )
48 .into_response()
49 }
50}
51
52impl GenericError {
53 pub fn new(code: StatusCode, err: impl std::error::Error + 'static) -> Self {
54 Self {
55 error: Box::new(err),
56 code,
57 }
58 }
59}
60
61pub struct InternalError {
62 error: Box<dyn std::error::Error + 'static>,
63}
64
65impl IntoResponse for InternalError {
66 fn into_response(self) -> Response {
67 tracing::error!(message = &*self.error);
68 let event_id = SentryEventID::for_last_event();
69 let context = build_context(&*self.error);
70 let context_text = format!("{context}");
71
72 (
73 StatusCode::INTERNAL_SERVER_ERROR,
74 TypedHeader(ContentType::text()),
75 event_id,
76 Extension(context),
77 context_text,
78 )
79 .into_response()
80 }
81}
82
83impl<E: std::error::Error + 'static> From<E> for InternalError {
84 fn from(err: E) -> Self {
85 Self {
86 error: Box::new(err),
87 }
88 }
89}
90
91impl InternalError {
92 #[must_use]
94 pub fn new(error: Box<dyn std::error::Error + 'static>) -> Self {
95 Self { error }
96 }
97
98 #[must_use]
100 pub fn from_anyhow(err: anyhow::Error) -> Self {
101 Self {
102 error: err.into_boxed_dyn_error(),
103 }
104 }
105}