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}