1use crate::xml::DavResponse;
8
9#[derive(Debug, Clone)]
14pub enum DavError {
15 Unauthorized,
18 Forbidden,
20 NotFound,
22 BadRequest(String),
24 Conflict,
26 PreconditionFailed,
28 MethodNotAllowed,
30 ServiceUnavailable,
32 ServerError(String),
34}
35
36impl DavError {
37 pub fn to_dav_response(&self) -> DavResponse {
42 let (status, body) = match self {
43 DavError::Unauthorized => (401, "authentication required"),
44 DavError::Forbidden => (403, "forbidden"),
45 DavError::NotFound => (404, "not found"),
46 DavError::BadRequest(_) => (400, "bad request"),
47 DavError::Conflict => (409, "conflict"),
48 DavError::PreconditionFailed => (412, "precondition failed"),
49 DavError::MethodNotAllowed => (405, "method not allowed"),
50 DavError::ServiceUnavailable => (503, "service unavailable"),
51 DavError::ServerError(_) => (500, "internal server error"),
52 };
53 let mut resp = DavResponse::new(status).with_body(body.as_bytes().to_vec());
54 if matches!(self, DavError::Unauthorized) {
55 resp = resp.with_header("www-authenticate", "Basic realm=\"mailrs-dav\"");
56 }
57 resp
58 }
59}
60
61impl std::fmt::Display for DavError {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 DavError::Unauthorized => write!(f, "unauthorized"),
65 DavError::Forbidden => write!(f, "forbidden"),
66 DavError::NotFound => write!(f, "not found"),
67 DavError::BadRequest(d) => write!(f, "bad request: {d}"),
68 DavError::Conflict => write!(f, "conflict"),
69 DavError::PreconditionFailed => write!(f, "precondition failed"),
70 DavError::MethodNotAllowed => write!(f, "method not allowed"),
71 DavError::ServiceUnavailable => write!(f, "service unavailable"),
72 DavError::ServerError(d) => write!(f, "server error: {d}"),
73 }
74 }
75}
76
77impl std::error::Error for DavError {}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn unauthorized_response_has_auth_header() {
85 let resp = DavError::Unauthorized.to_dav_response();
86 assert_eq!(resp.status, 401);
87 assert!(
88 resp.headers
89 .iter()
90 .any(|(k, _)| k.eq_ignore_ascii_case("www-authenticate"))
91 );
92 }
93
94 #[test]
95 fn not_found_maps_to_404() {
96 assert_eq!(DavError::NotFound.to_dav_response().status, 404);
97 }
98
99 #[test]
100 fn precondition_failed_maps_to_412() {
101 assert_eq!(DavError::PreconditionFailed.to_dav_response().status, 412);
102 }
103
104 #[test]
105 fn server_error_does_not_leak_description() {
106 let resp = DavError::ServerError("db borked".into()).to_dav_response();
108 assert_eq!(resp.status, 500);
109 assert!(!String::from_utf8_lossy(&resp.body).contains("borked"));
110 }
111}