use http::Method;
use crate::{
Depth,
dav::WebDavError,
names,
requests::{DavRequest, ParseResponseError, PreparedRequest, xml_content_type_header},
xmlutils::{XmlNode, validate_xml_response},
};
pub struct GetSupportedCalendarData<'a> {
href: &'a str,
}
impl<'a> GetSupportedCalendarData<'a> {
#[must_use]
pub fn new(href: &'a str) -> Self {
Self { href }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CalendarDataType {
pub content_type: String,
pub version: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GetSupportedCalendarDataResponse {
pub data_types: Vec<CalendarDataType>,
}
impl DavRequest for GetSupportedCalendarData<'_> {
type Response = GetSupportedCalendarDataResponse;
type ParseError = ParseResponseError;
type Error<E> = WebDavError<E>;
fn prepare_request(&self) -> Result<PreparedRequest, http::Error> {
let mut prop = XmlNode::new(&names::PROP);
prop.children = vec![XmlNode::new(&names::SUPPORTED_CALENDAR_DATA)];
let mut propfind = XmlNode::new(&names::PROPFIND);
propfind.children = vec![prop];
Ok(PreparedRequest {
method: Method::from_bytes(b"PROPFIND")?,
path: self.href.to_string(),
body: propfind.render_node(),
headers: vec![
("Depth".to_string(), Depth::Zero.to_string()),
xml_content_type_header(),
],
})
}
fn parse_response(
&self,
parts: &http::response::Parts,
body: &[u8],
) -> Result<Self::Response, ParseResponseError> {
let doc = validate_xml_response(parts, body)?;
let root = doc.root_element();
let mut data_types = Vec::new();
if let Some(prop) = root
.descendants()
.find(|node| node.tag_name() == names::SUPPORTED_CALENDAR_DATA)
{
for elem in prop
.descendants()
.filter(|node| node.tag_name().name() == "calendar-data")
{
if let Some(content_type) = elem.attribute("content-type") {
let version = elem.attribute("version").map(str::to_string);
data_types.push(CalendarDataType {
content_type: content_type.to_string(),
version,
});
}
}
}
Ok(GetSupportedCalendarDataResponse { data_types })
}
}
#[cfg(test)]
mod tests {
use super::*;
use http::StatusCode;
#[test]
fn test_parse_response() {
let req = GetSupportedCalendarData::new("/calendars/user/work/");
let response_xml = r#"<?xml version="1.0"?>
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<response>
<href>/calendars/user/work/</href>
<propstat>
<prop>
<C:supported-calendar-data>
<C:calendar-data content-type="text/calendar" version="2.0"/>
</C:supported-calendar-data>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
</multistatus>"#;
let response = http::Response::builder()
.status(StatusCode::MULTI_STATUS)
.body(())
.unwrap();
let (parts, ()) = response.into_parts();
let result = req.parse_response(&parts, response_xml.as_bytes()).unwrap();
assert_eq!(result.data_types.len(), 1);
assert_eq!(result.data_types[0].content_type, "text/calendar");
assert_eq!(result.data_types[0].version, Some("2.0".to_string()));
}
#[test]
fn test_parse_response_multiple_types() {
let req = GetSupportedCalendarData::new("/calendars/user/work/");
let response_xml = r#"<?xml version="1.0"?>
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<response>
<href>/calendars/user/work/</href>
<propstat>
<prop>
<C:supported-calendar-data>
<C:calendar-data content-type="text/calendar" version="2.0"/>
<C:calendar-data content-type="text/calendar" version="1.0"/>
</C:supported-calendar-data>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
</multistatus>"#;
let response = http::Response::builder()
.status(StatusCode::MULTI_STATUS)
.body(())
.unwrap();
let (parts, ()) = response.into_parts();
let result = req.parse_response(&parts, response_xml.as_bytes()).unwrap();
assert_eq!(result.data_types.len(), 2);
assert_eq!(result.data_types[0].version, Some("2.0".to_string()));
assert_eq!(result.data_types[1].version, Some("1.0".to_string()));
}
}