rustical_store/
error.rs

1use axum::response::IntoResponse;
2use http::StatusCode;
3use tracing::error;
4
5#[derive(Debug, thiserror::Error)]
6pub enum Error {
7    #[error("Not found")]
8    NotFound,
9
10    #[error("Resource already exists and overwrite=false")]
11    AlreadyExists,
12
13    #[error("Invalid principal type: {0}")]
14    InvalidPrincipalType(String),
15
16    #[error("Invalid principal id: Id cannot contain ':' or '$'.")]
17    InvalidPrincipalId,
18
19    #[error("Read-only")]
20    ReadOnly,
21
22    #[error("Error generating password hash")]
23    PasswordHash,
24
25    #[error(transparent)]
26    IO(#[from] std::io::Error),
27
28    #[error(transparent)]
29    Other(#[from] anyhow::Error),
30
31    #[error(transparent)]
32    IcalError(#[from] caldata::parser::ParserError),
33}
34
35impl Error {
36    #[must_use]
37    pub const fn status_code(&self) -> StatusCode {
38        match self {
39            Self::NotFound => StatusCode::NOT_FOUND,
40            Self::AlreadyExists => StatusCode::CONFLICT,
41            Self::ReadOnly => StatusCode::FORBIDDEN,
42            Self::IcalError(_err) => StatusCode::INTERNAL_SERVER_ERROR,
43            Self::InvalidPrincipalType(_) => StatusCode::BAD_REQUEST,
44            _ => StatusCode::INTERNAL_SERVER_ERROR,
45        }
46    }
47
48    #[must_use]
49    pub const fn is_not_found(&self) -> bool {
50        matches!(self, Self::NotFound)
51    }
52}
53
54impl IntoResponse for Error {
55    fn into_response(self) -> axum::response::Response {
56        if matches!(
57            self.status_code(),
58            StatusCode::INTERNAL_SERVER_ERROR | StatusCode::CONFLICT
59        ) {
60            error!("{self}");
61        }
62        (self.status_code(), self.to_string()).into_response()
63    }
64}