1
use axum::response::IntoResponse;
2
use http::StatusCode;
3
use sophia::{api::source::StreamError, iri::InvalidIri};
4
use std::{backtrace::Backtrace, error::Error, fmt::Display, path::PathBuf};
5

            
6
#[derive(Debug)]
7
pub(crate) enum MyError {
8
    Askama(askama::Error),
9
    NotFound(String),
10
    Request(reqwest::Error),
11
    InvalidIri(InvalidIri),
12
    StreamError(Box<dyn std::error::Error>),
13
    FormatError(Box<dyn std::error::Error>),
14
    IoError(PathBuf, std::io::Error, Backtrace),
15
    Error(Box<dyn Error>, Backtrace),
16
}
17

            
18
impl MyError {
19
    pub(crate) fn format_error(error: impl Error + 'static) -> Self {
20
        Self::FormatError(Box::new(error))
21
    }
22
    pub(crate) fn io_error(path: PathBuf, error: std::io::Error) -> Self {
23
        Self::IoError(path, error, Backtrace::capture())
24
    }
25
}
26

            
27
impl Display for MyError {
28
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29
        match self {
30
            Self::Askama(error) => error.fmt(f),
31
            Self::NotFound(msg) => msg.fmt(f),
32
            Self::IoError(path, e, _b) => write!(f, "{} {}", e, path.display()),
33
            Self::Error(e, _b) => e.fmt(f),
34
            _ => todo!(),
35
        }
36
    }
37
}
38

            
39
impl Error for MyError {}
40

            
41
impl From<askama::Error> for MyError {
42
    fn from(value: askama::Error) -> Self {
43
        Self::Askama(value)
44
    }
45
}
46

            
47
impl From<reqwest::Error> for MyError {
48
    fn from(value: reqwest::Error) -> Self {
49
        Self::Request(value)
50
    }
51
}
52

            
53
impl From<InvalidIri> for MyError {
54
    fn from(value: InvalidIri) -> Self {
55
        Self::InvalidIri(value)
56
    }
57
}
58

            
59
impl<A: std::error::Error + 'static, B: std::error::Error + 'static> From<StreamError<A, B>>
60
    for MyError
61
{
62
    fn from(value: StreamError<A, B>) -> Self {
63
        Self::StreamError(Box::new(value))
64
    }
65
}
66

            
67
impl From<Box<dyn std::error::Error>> for MyError {
68
    fn from(value: Box<dyn std::error::Error>) -> Self {
69
        Self::Error(value, Backtrace::capture())
70
    }
71
}
72

            
73
fn error_string(error: &dyn Error, backtrace: Option<Backtrace>) -> String {
74
    let mut msg = format!("Something went wrong: {}", error);
75
    if let Some(source) = error.source() {
76
        msg += &error_string(source, None);
77
    }
78
    if let Some(backtrace) = backtrace {
79
        msg += &format!("{}", backtrace);
80
    }
81
    msg
82
}
83

            
84
impl IntoResponse for MyError {
85
1
    fn into_response(self) -> axum::response::Response {
86
1
        match self {
87
            Self::Askama(error) => (
88
                StatusCode::INTERNAL_SERVER_ERROR,
89
                error_string(&error, None),
90
            )
91
                .into_response(),
92
1
            Self::NotFound(msg) => (StatusCode::NOT_FOUND, msg).into_response(),
93
            Self::IoError(path, error, backtrace) => (
94
                StatusCode::INTERNAL_SERVER_ERROR,
95
                format!("{} {} {}", path.display(), error, backtrace),
96
            )
97
                .into_response(),
98
            Self::Error(error, backtrace) => (
99
                StatusCode::INTERNAL_SERVER_ERROR,
100
                error_string(error.as_ref(), Some(backtrace)),
101
            )
102
                .into_response(),
103
            Self::Request(error) => (
104
                StatusCode::INTERNAL_SERVER_ERROR,
105
                error_string(&error, None),
106
            )
107
                .into_response(),
108
            _ => (StatusCode::INTERNAL_SERVER_ERROR, "Unknown error").into_response(),
109
        }
110
1
    }
111
}