rci/
error.rs

1use std::fmt;
2
3#[allow(clippy::module_name_repetitions)]
4#[derive(Debug)]
5pub enum Error {
6    Other(String),
7    Db(String),
8    Params(String),
9    Busy(String),
10    NotFound(String),
11    Ser(String),
12    Access(String),
13}
14
15impl fmt::Display for Error {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        match self {
18            Error::Other(s) | Error::Busy(s) => write!(f, "{}", s),
19            Error::Db(s) => write!(f, "database error: {}", s),
20            Error::Params(s) => write!(f, "invalid params: {}", s),
21            Error::NotFound(s) => write!(f, "not found: {}", s),
22            Error::Ser(s) => write!(f, "serde error: {}", s),
23            Error::Access(s) => write!(f, "access error: {}", s),
24        }
25    }
26}
27
28impl std::error::Error for Error {}
29
30impl Error {
31    pub fn other(e: impl fmt::Display) -> Self {
32        Self::Other(e.to_string())
33    }
34    pub fn not_found(e: impl fmt::Display) -> Self {
35        Self::NotFound(e.to_string())
36    }
37    pub fn ser<E: std::error::Error>(e: E) -> Self {
38        Self::Ser(e.to_string())
39    }
40    pub fn db<E: std::error::Error>(e: E) -> Self {
41        Self::Db(e.to_string())
42    }
43    pub fn params(e: impl fmt::Display) -> Self {
44        Self::Params(e.to_string())
45    }
46    pub fn access(e: impl fmt::Display) -> Self {
47        Self::Access(e.to_string())
48    }
49    pub fn busy(e: impl fmt::Display) -> Self {
50        Self::Busy(e.to_string())
51    }
52}
53
54#[cfg(feature = "ci")]
55impl From<async_channel::RecvError> for Error {
56    fn from(e: async_channel::RecvError) -> Self {
57        Self::other(e)
58    }
59}
60
61#[cfg(feature = "ci")]
62impl From<sqlx::Error> for Error {
63    fn from(e: sqlx::Error) -> Self {
64        Self::db(e)
65    }
66}
67
68#[cfg(feature = "server")]
69impl From<regex::Error> for Error {
70    fn from(e: regex::Error) -> Self {
71        Self::params(e)
72    }
73}
74
75#[cfg(feature = "ci")]
76impl From<serde_json::Error> for Error {
77    fn from(e: serde_json::Error) -> Self {
78        Self::ser(e)
79    }
80}
81
82impl From<std::num::TryFromIntError> for Error {
83    fn from(e: std::num::TryFromIntError) -> Self {
84        Self::other(e)
85    }
86}
87
88impl From<std::fmt::Error> for Error {
89    fn from(e: std::fmt::Error) -> Self {
90        Self::other(e)
91    }
92}
93
94impl From<std::io::Error> for Error {
95    fn from(e: std::io::Error) -> Self {
96        if e.kind() == std::io::ErrorKind::NotFound {
97            Self::not_found(e)
98        } else {
99            Self::other(e)
100        }
101    }
102}
103
104#[cfg(feature = "http")]
105pub mod http {
106    use super::Error as RciError;
107    use axum::http::StatusCode;
108
109    pub type Error = (StatusCode, String);
110
111    impl From<RciError> for Error {
112        fn from(e: RciError) -> Self {
113            match e {
114                RciError::NotFound(s) => (StatusCode::NOT_FOUND, s),
115                RciError::Params(s) => (StatusCode::BAD_REQUEST, s),
116                RciError::Busy(s) => (StatusCode::LOCKED, s),
117                RciError::Other(s) | RciError::Db(s) | RciError::Ser(s) => {
118                    (StatusCode::INTERNAL_SERVER_ERROR, s)
119                }
120                RciError::Access(s) => (StatusCode::FORBIDDEN, s),
121            }
122        }
123    }
124}