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("Read-only")]
17 ReadOnly,
18
19 #[error("Error generating password hash")]
20 PasswordHash,
21
22 #[error(transparent)]
23 IO(#[from] std::io::Error),
24
25 #[error(transparent)]
26 Other(#[from] anyhow::Error),
27
28 #[error(transparent)]
29 IcalError(#[from] caldata::parser::ParserError),
30}
31
32impl Error {
33 #[must_use]
34 pub const fn status_code(&self) -> StatusCode {
35 match self {
36 Self::NotFound => StatusCode::NOT_FOUND,
37 Self::AlreadyExists => StatusCode::CONFLICT,
38 Self::ReadOnly => StatusCode::FORBIDDEN,
39 Self::IcalError(_err) => StatusCode::INTERNAL_SERVER_ERROR,
40 Self::InvalidPrincipalType(_) => StatusCode::BAD_REQUEST,
41 _ => StatusCode::INTERNAL_SERVER_ERROR,
42 }
43 }
44
45 #[must_use]
46 pub const fn is_not_found(&self) -> bool {
47 matches!(self, Self::NotFound)
48 }
49}
50
51impl IntoResponse for Error {
52 fn into_response(self) -> axum::response::Response {
53 if matches!(
54 self.status_code(),
55 StatusCode::INTERNAL_SERVER_ERROR | StatusCode::CONFLICT
56 ) {
57 error!("{self}");
58 }
59 (self.status_code(), self.to_string()).into_response()
60 }
61}