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}