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 | Self::Forbidden => StatusCode::FORBIDDEN,
48 }
49 }
50}
51
52impl axum::response::IntoResponse for Error {
53 fn into_response(self) -> axum::response::Response {
54 if matches!(self.status_code(), StatusCode::INTERNAL_SERVER_ERROR) {
55 error!("{self}");
56 }
57
58 let mut resp = axum::response::Response::builder().status(self.status_code());
59 if matches!(&self, &Self::Unauthorized) {
60 resp.headers_mut()
61 .expect("This must always work")
62 .insert("WWW-Authenticate", "Basic".parse().unwrap());
63 }
64
65 resp.body(Body::new(self.to_string()))
66 .expect("This should always work")
67 }
68}