Skip to main content

rustical_store/
error.rs

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