rustical_dav/
error.rs

1use axum::body::Body;
2use http::StatusCode;
3use rustical_xml::XmlError;
4use thiserror::Error;
5use tracing::error;
6
7#[derive(Debug, Error)]
8pub enum Error {
9    #[error("Not found")]
10    NotFound,
11
12    #[error("Bad request: {0}")]
13    BadRequest(String),
14
15    #[error("Unauthorized")]
16    Unauthorized,
17
18    #[error("Internal server error :(")]
19    InternalError,
20
21    #[error("prop is read-only")]
22    PropReadOnly,
23
24    #[error(transparent)]
25    XmlError(#[from] rustical_xml::XmlError),
26
27    #[error(transparent)]
28    IOError(#[from] std::io::Error),
29
30    #[error("Precondition Failed")]
31    PreconditionFailed,
32
33    #[error("Forbidden")]
34    Forbidden,
35}
36
37impl Error {
38    pub fn status_code(&self) -> StatusCode {
39        match self {
40            Self::InternalError => StatusCode::INTERNAL_SERVER_ERROR,
41            Self::NotFound => StatusCode::NOT_FOUND,
42            Self::BadRequest(_) => StatusCode::BAD_REQUEST,
43            Self::Unauthorized => StatusCode::UNAUTHORIZED,
44            Self::XmlError(error) => match &error {
45                XmlError::InvalidTag(..)
46                | XmlError::MissingField(_)
47                | XmlError::UnsupportedEvent(_)
48                | XmlError::InvalidVariant(_)
49                | XmlError::InvalidFieldName(_, _)
50                | XmlError::InvalidValue(_) => StatusCode::UNPROCESSABLE_ENTITY,
51                _ => StatusCode::BAD_REQUEST,
52            },
53            Error::PropReadOnly => StatusCode::CONFLICT,
54            Error::PreconditionFailed => StatusCode::PRECONDITION_FAILED,
55            Self::IOError(_) => StatusCode::INTERNAL_SERVER_ERROR,
56            Self::Forbidden => StatusCode::FORBIDDEN,
57        }
58    }
59}
60
61impl axum::response::IntoResponse for Error {
62    fn into_response(self) -> axum::response::Response {
63        if matches!(
64            self.status_code(),
65            StatusCode::INTERNAL_SERVER_ERROR | StatusCode::PRECONDITION_FAILED
66        ) {
67            error!("{self}");
68        }
69
70        let mut resp = axum::response::Response::builder().status(self.status_code());
71        if matches!(&self, &Error::Unauthorized) {
72            resp.headers_mut()
73                .expect("This must always work")
74                .insert("WWW-Authenticate", "Basic".parse().unwrap());
75        }
76
77        resp.body(Body::new(self.to_string()))
78            .expect("This should always work")
79    }
80}