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}