mas_axum_utils/
fancy_error.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use 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}