Skip to main content

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("Bad request: {0}")]
10    BadRequest(String),
11
12    #[error("Unauthorized")]
13    Unauthorized,
14
15    #[error("prop is read-only")]
16    PropReadOnly,
17
18    #[error(transparent)]
19    XmlError(#[from] rustical_xml::XmlError),
20
21    #[error("Precondition Failed")]
22    PreconditionFailed,
23
24    #[error("Forbidden")]
25    Forbidden,
26}
27
28impl Error {
29    #[must_use]
30    pub const fn status_code(&self) -> StatusCode {
31        match self {
32            Self::BadRequest(_) => StatusCode::BAD_REQUEST,
33            Self::Unauthorized => StatusCode::UNAUTHORIZED,
34            Self::XmlError(error) => match &error {
35                XmlError::InvalidTag(..)
36                | XmlError::MissingField(_)
37                | XmlError::UnsupportedEvent(_)
38                | XmlError::InvalidVariant(_)
39                | XmlError::InvalidFieldName(_, _)
40                | XmlError::InvalidValue(_) => StatusCode::UNPROCESSABLE_ENTITY,
41                _ => StatusCode::BAD_REQUEST,
42            },
43            Self::PropReadOnly => StatusCode::CONFLICT,
44            Self::PreconditionFailed => StatusCode::PRECONDITION_FAILED,
45            Self::Forbidden => StatusCode::FORBIDDEN,
46        }
47    }
48}
49
50impl axum::response::IntoResponse for Error {
51    fn into_response(self) -> axum::response::Response {
52        if matches!(self.status_code(), StatusCode::INTERNAL_SERVER_ERROR) {
53            error!("{self}");
54        }
55
56        let mut resp = axum::response::Response::builder().status(self.status_code());
57        if matches!(&self, &Self::Unauthorized) {
58            resp.headers_mut()
59                .expect("This must always work")
60                .insert("WWW-Authenticate", "Basic".parse().unwrap());
61        }
62
63        resp.body(Body::new(self.to_string()))
64            .expect("This should always work")
65    }
66}