diff --git a/src/dav/calencoder.rs b/src/dav/calencoder.rs index 114eee9..cadfc78 100644 --- a/src/dav/calencoder.rs +++ b/src/dav/calencoder.rs @@ -5,7 +5,7 @@ use tokio::io::AsyncWrite; use super::caltypes::*; use super::xml::{QWrite, IWrite, Writer}; -use super::types::Extension; +use super::types::{Extension, Node}; const ICAL_DATETIME_FMT: &str = "%Y%m%dT%H%M%SZ"; @@ -23,7 +23,7 @@ impl QWrite for MkCalendar { } } -impl QWrite for MkCalendarResponse { +impl> QWrite for MkCalendarResponse { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_cal_element("mkcalendar-response"); let end = start.to_end(); @@ -810,19 +810,19 @@ mod tests { #[tokio::test] async fn rfc_calendar_query1_res() { let got = serialize( - &dav::Multistatus:: { + &dav::Multistatus::> { responses: vec![ dav::Response { status_or_propstat: dav::StatusOrPropstat::PropStat( dav::Href("http://cal.example.com/bernard/work/abcd2.ics".into()), vec![dav::PropStat { - prop: dav::AnyProp::Value(dav::PropValue(vec![ + prop: dav::PropValue(vec![ dav::Property::GetEtag("\"fffff-abcd2\"".into()), dav::Property::Extension(Property::CalendarData(CalendarDataPayload { mime: None, payload: "PLACEHOLDER".into() })), - ])), + ]), status: dav::Status(http::status::StatusCode::OK), error: None, responsedescription: None, @@ -836,13 +836,13 @@ mod tests { status_or_propstat: dav::StatusOrPropstat::PropStat( dav::Href("http://cal.example.com/bernard/work/abcd3.ics".into()), vec![dav::PropStat { - prop: dav::AnyProp::Value(dav::PropValue(vec![ + prop: dav::PropValue(vec![ dav::Property::GetEtag("\"fffff-abcd3\"".into()), dav::Property::Extension(Property::CalendarData(CalendarDataPayload{ mime: None, payload: "PLACEHOLDER".into(), })), - ])), + ]), status: dav::Status(http::status::StatusCode::OK), error: None, responsedescription: None, diff --git a/src/dav/caltypes.rs b/src/dav/caltypes.rs index 68e7baf..d9cbb12 100644 --- a/src/dav/caltypes.rs +++ b/src/dav/caltypes.rs @@ -44,7 +44,7 @@ pub struct MkCalendar(pub dav::Set); /// /// #[derive(Debug, PartialEq)] -pub struct MkCalendarResponse(pub Vec>); +pub struct MkCalendarResponse>(pub Vec>); // --- (REPORT PART) --- diff --git a/src/dav/decoder.rs b/src/dav/decoder.rs index 66c0839..41eca36 100644 --- a/src/dav/decoder.rs +++ b/src/dav/decoder.rs @@ -84,7 +84,7 @@ impl QRead> for PropertyUpdate { } /// Generic response -impl QRead> for Multistatus { +impl> QRead> for Multistatus { async fn qread(xml: &mut Reader) -> Result, ParsingError> { xml.tag_start(DAV_URN, "multistatus").await?; let mut responses = Vec::new(); @@ -181,16 +181,74 @@ impl QRead> for Error { // ---- INNER XML -impl QRead> for Response { +impl> QRead> for Response { async fn qread(xml: &mut Reader) -> Result, ParsingError> { if xml.maybe_tag_start(DAV_URN, "response").await?.is_none() { return Ok(None) } + let (mut status, mut error, mut responsedescription, mut location) = (None, None, None, None); + let mut href = Vec::new(); + let mut propstat = Vec::new(); + loop { + if let Some(v) = Status::qread(xml).await? { + status = Some(v); + } else if let Some(v) = Href::qread(xml).await? { + href.push(v); + } else if let Some(v) = PropStat::qread(xml).await? { + propstat.push(v); + } else if let Some(v) = Error::qread(xml).await? { + error = Some(v); + } else if let Some(v) = ResponseDescription::qread(xml).await? { + responsedescription = Some(v); + } else if let Some(v) = Location::qread(xml).await? { + location = Some(v); + } else { + match xml.peek() { + Event::End(_) => break, + _ => { xml.skip().await? }, + }; + } + } + + xml.tag_stop(DAV_URN, "response").await?; + match (status, &propstat[..], &href[..]) { + (Some(status), &[], &[_, ..]) => Ok(Some(Response { + status_or_propstat: StatusOrPropstat::Status(href, status), + error, responsedescription, location, + })), + (None, &[_, ..], &[_, ..]) => Ok(Some(Response { + status_or_propstat: StatusOrPropstat::PropStat(href.into_iter().next().unwrap(), propstat), + error, responsedescription, location, + })), + (Some(_), &[_, ..], _) => Err(ParsingError::InvalidValue), + _ => Err(ParsingError::MissingChild), + } + } +} + +impl> QRead> for PropStat { + async fn qread(xml: &mut Reader) -> Result, ParsingError> { + if xml.maybe_tag_start(DAV_URN, "propstat").await?.is_none() { + return Ok(None) + } unimplemented!(); } } +impl QRead for Status { + async fn qread(xml: &mut Reader) -> Result, ParsingError> { + if xml.maybe_tag_start(DAV_URN, "status").await?.is_none() { + return Ok(None) + } + let fullcode = xml.tag_string().await?; + let txtcode = fullcode.splitn(3, ' ').nth(1).ok_or(ParsingError::InvalidValue)?; + let code = http::status::StatusCode::from_bytes(txtcode.as_bytes()).or(Err(ParsingError::InvalidValue))?; + xml.tag_stop(DAV_URN, "status").await?; + Ok(Some(Status(code))) + } +} + impl QRead for ResponseDescription { async fn qread(xml: &mut Reader) -> Result, ParsingError> { if xml.maybe_tag_start(DAV_URN, "responsedescription").await?.is_none() { @@ -202,6 +260,26 @@ impl QRead for ResponseDescription { } } +impl QRead for Location { + async fn qread(xml: &mut Reader) -> Result, ParsingError> { + if xml.maybe_tag_start(DAV_URN, "location").await?.is_none() { + return Ok(None) + } + let href = loop { + if let Some(v) = Href::qread(xml).await? { + break v + } + + match xml.peek() { + Event::End(_) => return Err(ParsingError::MissingChild), + _ => xml.skip().await?, + }; + }; + xml.tag_stop(DAV_URN, "location").await?; + Ok(Some(Location(href))) + } +} + impl QRead> for PropertyUpdateItem { async fn qread(xml: &mut Reader) -> Result, ParsingError> { if let Some(rm) = Remove::qread(xml).await? { diff --git a/src/dav/encoder.rs b/src/dav/encoder.rs index 5736217..9e60f29 100644 --- a/src/dav/encoder.rs +++ b/src/dav/encoder.rs @@ -53,7 +53,7 @@ impl QWrite for PropertyUpdate { /// PROPFIND RESPONSE, PROPPATCH RESPONSE, COPY RESPONSE, MOVE RESPONSE /// DELETE RESPONSE, -impl QWrite for Multistatus { +impl> QWrite for Multistatus { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("multistatus"); let end = start.to_end(); @@ -146,15 +146,6 @@ impl QWrite for Remove { } -impl QWrite for AnyProp { - async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { - match self { - Self::Name(propname) => propname.qwrite(xml).await, - Self::Value(propval) => propval.qwrite(xml).await, - } - } -} - impl QWrite for PropName { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("prop"); @@ -180,7 +171,7 @@ impl QWrite for Href { } } -impl QWrite for Response { +impl> QWrite for Response { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("response"); let end = start.to_end(); @@ -200,7 +191,7 @@ impl QWrite for Response { } } -impl QWrite for StatusOrPropstat { +impl> QWrite for StatusOrPropstat { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { match self { Self::Status(many_href, status) => { @@ -258,7 +249,7 @@ impl QWrite for Location { } } -impl QWrite for PropStat { +impl> QWrite for PropStat { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("propstat"); let end = start.to_end(); @@ -681,7 +672,7 @@ mod tests { #[tokio::test] async fn basic_multistatus() { let got = serialize( - &Multistatus:: { + &Multistatus::> { responses: vec![], responsedescription: Some(ResponseDescription("Hello world".into())) }, @@ -730,18 +721,18 @@ mod tests { #[tokio::test] async fn rfc_propname_res() { let got = serialize( - &Multistatus:: { + &Multistatus::> { responses: vec![ Response { status_or_propstat: StatusOrPropstat::PropStat( Href("http://www.example.com/container/".into()), vec![PropStat { - prop: AnyProp::Name(PropName(vec![ + prop: PropName(vec![ PropertyRequest::CreationDate, PropertyRequest::DisplayName, PropertyRequest::ResourceType, PropertyRequest::SupportedLock, - ])), + ]), status: Status(http::status::StatusCode::OK), error: None, responsedescription: None, @@ -755,7 +746,7 @@ mod tests { status_or_propstat: StatusOrPropstat::PropStat( Href("http://www.example.com/container/front.html".into()), vec![PropStat { - prop: AnyProp::Name(PropName(vec![ + prop: PropName(vec![ PropertyRequest::CreationDate, PropertyRequest::DisplayName, PropertyRequest::GetContentLength, @@ -764,7 +755,7 @@ mod tests { PropertyRequest::GetLastModified, PropertyRequest::ResourceType, PropertyRequest::SupportedLock, - ])), + ]), status: Status(http::status::StatusCode::OK), error: None, responsedescription: None, @@ -831,13 +822,13 @@ mod tests { async fn rfc_allprop_res() { use chrono::{DateTime,FixedOffset,TimeZone}; let got = serialize( - &Multistatus:: { + &Multistatus::> { responses: vec![ Response { status_or_propstat: StatusOrPropstat::PropStat( Href("/container/".into()), vec![PropStat { - prop: AnyProp::Value(PropValue(vec![ + prop: PropValue(vec![ Property::CreationDate(FixedOffset::west_opt(8 * 3600) .unwrap() .with_ymd_and_hms(1997, 12, 1, 17, 42, 21) @@ -854,7 +845,7 @@ mod tests { locktype: LockType::Write, }, ]), - ])), + ]), status: Status(http::status::StatusCode::OK), error: None, responsedescription: None, @@ -868,7 +859,7 @@ mod tests { status_or_propstat: StatusOrPropstat::PropStat( Href("/container/front.html".into()), vec![PropStat { - prop: AnyProp::Value(PropValue(vec![ + prop: PropValue(vec![ Property::CreationDate(FixedOffset::west_opt(8 * 3600) .unwrap() .with_ymd_and_hms(1997, 12, 1, 18, 27, 21) @@ -892,7 +883,7 @@ mod tests { locktype: LockType::Write, }, ]), - ])), + ]), status: Status(http::status::StatusCode::OK), error: None, responsedescription: None, @@ -1029,7 +1020,7 @@ mod tests { #[tokio::test] async fn rfc_delete_locked2() { let got = serialize( - &Multistatus:: { + &Multistatus::> { responses: vec![Response { status_or_propstat: StatusOrPropstat::Status( vec![Href("http://www.example.com/container/resource3".into())], diff --git a/src/dav/types.rs b/src/dav/types.rs index b3842de..246a4bd 100644 --- a/src/dav/types.rs +++ b/src/dav/types.rs @@ -7,12 +7,12 @@ use super::error; /// It's how we implement a DAV extension /// (That's the dark magic part...) -pub trait ExtensionItem = xml::QRead + xml::QWrite + Debug + PartialEq; +pub trait Node = xml::QRead + xml::QWrite + Debug + PartialEq; pub trait Extension { - type Error: ExtensionItem; - type Property: ExtensionItem; - type PropertyRequest: ExtensionItem; - type ResourceType: ExtensionItem; + type Error: Node; + type Property: Node; + type PropertyRequest: Node; + type ResourceType: Node; } /// 14.1. activelock XML Element @@ -333,8 +333,8 @@ pub enum LockType { /// /// #[derive(Debug, PartialEq)] -pub struct Multistatus { - pub responses: Vec>, +pub struct Multistatus> { + pub responses: Vec>, pub responsedescription: Option, } @@ -383,12 +383,6 @@ pub enum Owner { /// text or mixed content. /// /// -#[derive(Debug, PartialEq)] -pub enum AnyProp { - Name(PropName), - Value(PropValue), -} - #[derive(Debug, PartialEq)] pub struct PropName(pub Vec>); @@ -471,8 +465,8 @@ pub enum PropFind { /// /// #[derive(Debug, PartialEq)] -pub struct PropStat { - pub prop: AnyProp, +pub struct PropStat> { + pub prop: N, pub status: Status, pub error: Option>, pub responsedescription: Option, @@ -520,16 +514,16 @@ pub struct Remove(pub PropName); /// --- rewritten as --- /// #[derive(Debug, PartialEq)] -pub enum StatusOrPropstat { +pub enum StatusOrPropstat> { // One status, multiple hrefs... Status(Vec, Status), // A single href, multiple properties... - PropStat(Href, Vec>), + PropStat(Href, Vec>), } #[derive(Debug, PartialEq)] -pub struct Response { - pub status_or_propstat: StatusOrPropstat, +pub struct Response> { + pub status_or_propstat: StatusOrPropstat, pub error: Option>, pub responsedescription: Option, pub location: Option,