use crate::xml::DavResponse;
#[derive(Debug, Clone)]
pub enum DavError {
Unauthorized,
Forbidden,
NotFound,
BadRequest(String),
Conflict,
PreconditionFailed,
MethodNotAllowed,
ServiceUnavailable,
ServerError(String),
}
impl DavError {
pub fn to_dav_response(&self) -> DavResponse {
let (status, body) = match self {
DavError::Unauthorized => (401, "authentication required"),
DavError::Forbidden => (403, "forbidden"),
DavError::NotFound => (404, "not found"),
DavError::BadRequest(_) => (400, "bad request"),
DavError::Conflict => (409, "conflict"),
DavError::PreconditionFailed => (412, "precondition failed"),
DavError::MethodNotAllowed => (405, "method not allowed"),
DavError::ServiceUnavailable => (503, "service unavailable"),
DavError::ServerError(_) => (500, "internal server error"),
};
let mut resp = DavResponse::new(status).with_body(body.as_bytes().to_vec());
if matches!(self, DavError::Unauthorized) {
resp = resp.with_header("www-authenticate", "Basic realm=\"mailrs-dav\"");
}
resp
}
}
impl std::fmt::Display for DavError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DavError::Unauthorized => write!(f, "unauthorized"),
DavError::Forbidden => write!(f, "forbidden"),
DavError::NotFound => write!(f, "not found"),
DavError::BadRequest(d) => write!(f, "bad request: {d}"),
DavError::Conflict => write!(f, "conflict"),
DavError::PreconditionFailed => write!(f, "precondition failed"),
DavError::MethodNotAllowed => write!(f, "method not allowed"),
DavError::ServiceUnavailable => write!(f, "service unavailable"),
DavError::ServerError(d) => write!(f, "server error: {d}"),
}
}
}
impl std::error::Error for DavError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unauthorized_response_has_auth_header() {
let resp = DavError::Unauthorized.to_dav_response();
assert_eq!(resp.status, 401);
assert!(
resp.headers
.iter()
.any(|(k, _)| k.eq_ignore_ascii_case("www-authenticate"))
);
}
#[test]
fn not_found_maps_to_404() {
assert_eq!(DavError::NotFound.to_dav_response().status, 404);
}
#[test]
fn precondition_failed_maps_to_412() {
assert_eq!(DavError::PreconditionFailed.to_dav_response().status, 412);
}
#[test]
fn server_error_does_not_leak_description() {
let resp = DavError::ServerError("db borked".into()).to_dav_response();
assert_eq!(resp.status, 500);
assert!(!String::from_utf8_lossy(&resp.body).contains("borked"));
}
}