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
18pub struct FancyError {
19 context: ErrorContext,
20}
21
22impl FancyError {
23 #[must_use]
24 pub fn new(context: ErrorContext) -> Self {
25 Self { context }
26 }
27}
28
29impl std::fmt::Display for FancyError {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 let code = self.context.code().unwrap_or("Internal error");
32 match (self.context.description(), self.context.details()) {
33 (Some(description), Some(details)) => {
34 write!(f, "{code}: {description} ({details})")
35 }
36 (Some(message), None) | (None, Some(message)) => {
37 write!(f, "{code}: {message}")
38 }
39 (None, None) => {
40 write!(f, "{code}")
41 }
42 }
43 }
44}
45
46impl<E: std::fmt::Debug + std::fmt::Display> From<E> for FancyError {
47 fn from(err: E) -> Self {
48 let context = ErrorContext::new()
49 .with_description(format!("{err}"))
50 .with_details(format!("{err:?}"));
51 FancyError { context }
52 }
53}
54
55impl IntoResponse for FancyError {
56 fn into_response(self) -> Response {
57 let error = format!("{}", self.context);
58 let event_id = sentry::capture_message(&error, sentry::Level::Error);
59 (
60 StatusCode::INTERNAL_SERVER_ERROR,
61 TypedHeader(ContentType::text()),
62 SentryEventID::from(event_id),
63 Extension(self.context),
64 error,
65 )
66 .into_response()
67 }
68}