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
            Self::InvalidIri(e) => e.fmt(f),
35
            Self::StreamError(e) => e.fmt(f),
36
            Self::FormatError(e) => e.fmt(f),
37
            _ => todo!(),
38
        }
39
    }
40
}
41

            
42
impl Error for MyError {}
43

            
44
impl From<askama::Error> for MyError {
45
    fn from(value: askama::Error) -> Self {
46
        Self::Askama(value)
47
    }
48
}
49

            
50
impl From<reqwest::Error> for MyError {
51
    fn from(value: reqwest::Error) -> Self {
52
        Self::Request(value)
53
    }
54
}
55

            
56
impl From<InvalidIri> for MyError {
57
    fn from(value: InvalidIri) -> Self {
58
        Self::InvalidIri(value)
59
    }
60
}
61

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

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

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

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