From 1a43ce5ac7033c148f64a033f2b1d335e95e11d5 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Fri, 8 Mar 2024 08:17:03 +0100 Subject: WIP refactor --- aero-dav/src/decoder.rs | 947 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 947 insertions(+) create mode 100644 aero-dav/src/decoder.rs (limited to 'aero-dav/src/decoder.rs') diff --git a/aero-dav/src/decoder.rs b/aero-dav/src/decoder.rs new file mode 100644 index 0000000..65cb712 --- /dev/null +++ b/aero-dav/src/decoder.rs @@ -0,0 +1,947 @@ +use std::future::Future; + +use quick_xml::events::Event; +use quick_xml::events::attributes::AttrError; +use quick_xml::name::{Namespace, QName, PrefixDeclaration, ResolveResult, ResolveResult::*}; +use quick_xml::reader::NsReader; +use tokio::io::AsyncBufRead; + +use super::types::*; +use super::error::ParsingError; +use super::xml::{Node, QRead, Reader, IRead, DAV_URN, CAL_URN}; + +//@TODO (1) Rewrite all objects as Href, +// where we return Ok(None) instead of trying to find the object at any cost. +// Add a xml.find() -> Result, ParsingError> or similar for the cases we +// really need the object +// (2) Rewrite QRead and replace Result, _> with Result<_, _>, not found being a possible +// error. +// (3) Rewrite vectors with xml.collect() -> Result, _> +// (4) Something for alternatives would be great but no idea yet + +// ---- ROOT ---- + +/// Propfind request +impl QRead> for PropFind { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "propfind").await?; + let propfind: PropFind = loop { + // allprop + if let Some(_) = xml.maybe_open(DAV_URN, "allprop").await? { + let includ = xml.maybe_find::>().await?; + xml.close().await?; + break PropFind::AllProp(includ) + } + + // propname + if let Some(_) = xml.maybe_open(DAV_URN, "propname").await? { + xml.close().await?; + break PropFind::PropName + } + + // prop + let (mut maybe_prop, mut dirty) = (None, false); + xml.maybe_read::>(&mut maybe_prop, &mut dirty).await?; + if let Some(prop) = maybe_prop { + break PropFind::Prop(prop) + } + + // not found, skipping + xml.skip().await?; + }; + xml.close().await?; + + Ok(propfind) + } +} + +/// PROPPATCH request +impl QRead> for PropertyUpdate { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "propertyupdate").await?; + let collected_items = xml.collect::>().await?; + xml.close().await?; + Ok(PropertyUpdate(collected_items)) + } +} + +/// Generic response +impl> QRead> for Multistatus { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "multistatus").await?; + let mut responses = Vec::new(); + let mut responsedescription = None; + + loop { + let mut dirty = false; + xml.maybe_push(&mut responses, &mut dirty).await?; + xml.maybe_read(&mut responsedescription, &mut dirty).await?; + if !dirty { + match xml.peek() { + Event::End(_) => break, + _ => xml.skip().await?, + }; + } + } + + xml.close().await?; + Ok(Multistatus { responses, responsedescription }) + } +} + +// LOCK REQUEST +impl QRead for LockInfo { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "lockinfo").await?; + let (mut m_scope, mut m_type, mut owner) = (None, None, None); + loop { + let mut dirty = false; + xml.maybe_read::(&mut m_scope, &mut dirty).await?; + xml.maybe_read::(&mut m_type, &mut dirty).await?; + xml.maybe_read::(&mut owner, &mut dirty).await?; + + if !dirty { + match xml.peek() { + Event::End(_) => break, + _ => xml.skip().await?, + }; + } + } + xml.close().await?; + match (m_scope, m_type) { + (Some(lockscope), Some(locktype)) => Ok(LockInfo { lockscope, locktype, owner }), + _ => Err(ParsingError::MissingChild), + } + } +} + +// LOCK RESPONSE +impl QRead> for PropValue { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "prop").await?; + let mut acc = xml.collect::>().await?; + xml.close().await?; + Ok(PropValue(acc)) + } +} + + +/// Error response +impl QRead> for Error { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "error").await?; + let violations = xml.collect::>().await?; + xml.close().await?; + Ok(Error(violations)) + } +} + + + +// ---- INNER XML +impl> QRead> for Response { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "response").await?; + let (mut status, mut error, mut responsedescription, mut location) = (None, None, None, None); + let mut href = Vec::new(); + let mut propstat = Vec::new(); + + loop { + let mut dirty = false; + xml.maybe_read::(&mut status, &mut dirty).await?; + xml.maybe_push::(&mut href, &mut dirty).await?; + xml.maybe_push::>(&mut propstat, &mut dirty).await?; + xml.maybe_read::>(&mut error, &mut dirty).await?; + xml.maybe_read::(&mut responsedescription, &mut dirty).await?; + xml.maybe_read::(&mut location, &mut dirty).await?; + + if !dirty { + match xml.peek() { + Event::End(_) => break, + _ => { xml.skip().await? }, + }; + } + } + + xml.close().await?; + match (status, &propstat[..], &href[..]) { + (Some(status), &[], &[_, ..]) => Ok(Response { + status_or_propstat: StatusOrPropstat::Status(href, status), + error, responsedescription, location, + }), + (None, &[_, ..], &[_, ..]) => Ok(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 { + xml.open(DAV_URN, "propstat").await?; + + let (mut m_prop, mut m_status, mut error, mut responsedescription) = (None, None, None, None); + + loop { + let mut dirty = false; + xml.maybe_read::(&mut m_prop, &mut dirty).await?; + xml.maybe_read::(&mut m_status, &mut dirty).await?; + xml.maybe_read::>(&mut error, &mut dirty).await?; + xml.maybe_read::(&mut responsedescription, &mut dirty).await?; + + if !dirty { + match xml.peek() { + Event::End(_) => break, + _ => xml.skip().await?, + }; + } + } + + xml.close().await?; + match (m_prop, m_status) { + (Some(prop), Some(status)) => Ok(PropStat { prop, status, error, responsedescription }), + _ => Err(ParsingError::MissingChild), + } + } +} + +impl QRead for Status { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "status").await?; + 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.close().await?; + Ok(Status(code)) + } +} + +impl QRead for ResponseDescription { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "responsedescription").await?; + let cnt = xml.tag_string().await?; + xml.close().await?; + Ok(ResponseDescription(cnt)) + } +} + +impl QRead for Location { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "location").await?; + let href = xml.find::().await?; + xml.close().await?; + Ok(Location(href)) + } +} + +impl QRead> for PropertyUpdateItem { + async fn qread(xml: &mut Reader) -> Result { + match Remove::qread(xml).await { + Err(ParsingError::Recoverable) => (), + otherwise => return otherwise.map(PropertyUpdateItem::Remove), + } + Set::qread(xml).await.map(PropertyUpdateItem::Set) + } +} + +impl QRead> for Remove { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "remove").await?; + let propname = xml.find::>().await?; + xml.close().await?; + Ok(Remove(propname)) + } +} + +impl QRead> for Set { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "set").await?; + let propvalue = xml.find::>().await?; + xml.close().await?; + Ok(Set(propvalue)) + } +} + +impl QRead> for Violation { + async fn qread(xml: &mut Reader) -> Result { + if xml.maybe_open(DAV_URN, "lock-token-matches-request-uri").await?.is_some() { + xml.close().await?; + Ok(Violation::LockTokenMatchesRequestUri) + } else if xml.maybe_open(DAV_URN, "lock-token-submitted").await?.is_some() { + let links = xml.collect::().await?; + xml.close().await?; + Ok(Violation::LockTokenSubmitted(links)) + } else if xml.maybe_open(DAV_URN, "no-conflicting-lock").await?.is_some() { + let links = xml.collect::().await?; + xml.close().await?; + Ok(Violation::NoConflictingLock(links)) + } else if xml.maybe_open(DAV_URN, "no-external-entities").await?.is_some() { + xml.close().await?; + Ok(Violation::NoExternalEntities) + } else if xml.maybe_open(DAV_URN, "preserved-live-properties").await?.is_some() { + xml.close().await?; + Ok(Violation::PreservedLiveProperties) + } else if xml.maybe_open(DAV_URN, "propfind-finite-depth").await?.is_some() { + xml.close().await?; + Ok(Violation::PropfindFiniteDepth) + } else if xml.maybe_open(DAV_URN, "cannot-modify-protected-property").await?.is_some() { + xml.close().await?; + Ok(Violation::CannotModifyProtectedProperty) + } else { + E::Error::qread(xml).await.map(Violation::Extension) + } + } +} + +impl QRead> for Include { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "include").await?; + let acc = xml.collect::>().await?; + xml.close().await?; + Ok(Include(acc)) + } +} + +impl QRead> for PropName { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "prop").await?; + let acc = xml.collect::>().await?; + xml.close().await?; + Ok(PropName(acc)) + } +} + +impl QRead> for PropertyRequest { + async fn qread(xml: &mut Reader) -> Result { + let maybe = if xml.maybe_open(DAV_URN, "creationdate").await?.is_some() { + Some(PropertyRequest::CreationDate) + } else if xml.maybe_open(DAV_URN, "displayname").await?.is_some() { + Some(PropertyRequest::DisplayName) + } else if xml.maybe_open(DAV_URN, "getcontentlanguage").await?.is_some() { + Some(PropertyRequest::GetContentLanguage) + } else if xml.maybe_open(DAV_URN, "getcontentlength").await?.is_some() { + Some(PropertyRequest::GetContentLength) + } else if xml.maybe_open(DAV_URN, "getcontenttype").await?.is_some() { + Some(PropertyRequest::GetContentType) + } else if xml.maybe_open(DAV_URN, "getetag").await?.is_some() { + Some(PropertyRequest::GetEtag) + } else if xml.maybe_open(DAV_URN, "getlastmodified").await?.is_some() { + Some(PropertyRequest::GetLastModified) + } else if xml.maybe_open(DAV_URN, "lockdiscovery").await?.is_some() { + Some(PropertyRequest::LockDiscovery) + } else if xml.maybe_open(DAV_URN, "resourcetype").await?.is_some() { + Some(PropertyRequest::ResourceType) + } else if xml.maybe_open(DAV_URN, "supportedlock").await?.is_some() { + Some(PropertyRequest::SupportedLock) + } else { + None + }; + + match maybe { + Some(pr) => { + xml.close().await?; + Ok(pr) + }, + None => E::PropertyRequest::qread(xml).await.map(PropertyRequest::Extension), + } + } +} + +impl QRead> for Property { + async fn qread(xml: &mut Reader) -> Result { + use chrono::{DateTime, FixedOffset, TimeZone}; + + // Core WebDAV properties + if xml.maybe_open(DAV_URN, "creationdate").await?.is_some() { + let datestr = xml.tag_string().await?; + xml.close().await?; + return Ok(Property::CreationDate(DateTime::parse_from_rfc3339(datestr.as_str())?)) + } else if xml.maybe_open(DAV_URN, "displayname").await?.is_some() { + let name = xml.tag_string().await?; + xml.close().await?; + return Ok(Property::DisplayName(name)) + } else if xml.maybe_open(DAV_URN, "getcontentlanguage").await?.is_some() { + let lang = xml.tag_string().await?; + xml.close().await?; + return Ok(Property::GetContentLanguage(lang)) + } else if xml.maybe_open(DAV_URN, "getcontentlength").await?.is_some() { + let cl = xml.tag_string().await?.parse::()?; + xml.close().await?; + return Ok(Property::GetContentLength(cl)) + } else if xml.maybe_open(DAV_URN, "getcontenttype").await?.is_some() { + let ct = xml.tag_string().await?; + xml.close().await?; + return Ok(Property::GetContentType(ct)) + } else if xml.maybe_open(DAV_URN, "getetag").await?.is_some() { + let etag = xml.tag_string().await?; + xml.close().await?; + return Ok(Property::GetEtag(etag)) + } else if xml.maybe_open(DAV_URN, "getlastmodified").await?.is_some() { + let datestr = xml.tag_string().await?; + xml.close().await?; + return Ok(Property::GetLastModified(DateTime::parse_from_rfc2822(datestr.as_str())?)) + } else if xml.maybe_open(DAV_URN, "lockdiscovery").await?.is_some() { + let acc = xml.collect::().await?; + xml.close().await?; + return Ok(Property::LockDiscovery(acc)) + } else if xml.maybe_open(DAV_URN, "resourcetype").await?.is_some() { + let acc = xml.collect::>().await?; + xml.close().await?; + return Ok(Property::ResourceType(acc)) + } else if xml.maybe_open(DAV_URN, "supportedlock").await?.is_some() { + let acc = xml.collect::().await?; + xml.close().await?; + return Ok(Property::SupportedLock(acc)) + } + + // Option 2: an extension property, delegating + E::Property::qread(xml).await.map(Property::Extension) + } +} + +impl QRead for ActiveLock { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "activelock").await?; + let (mut m_scope, mut m_type, mut m_depth, mut owner, mut timeout, mut locktoken, mut m_root) = + (None, None, None, None, None, None, None); + + loop { + let mut dirty = false; + xml.maybe_read::(&mut m_scope, &mut dirty).await?; + xml.maybe_read::(&mut m_type, &mut dirty).await?; + xml.maybe_read::(&mut m_depth, &mut dirty).await?; + xml.maybe_read::(&mut owner, &mut dirty).await?; + xml.maybe_read::(&mut timeout, &mut dirty).await?; + xml.maybe_read::(&mut locktoken, &mut dirty).await?; + xml.maybe_read::(&mut m_root, &mut dirty).await?; + + if !dirty { + match xml.peek() { + Event::End(_) => break, + _ => { xml.skip().await?; }, + } + } + } + + xml.close().await?; + match (m_scope, m_type, m_depth, m_root) { + (Some(lockscope), Some(locktype), Some(depth), Some(lockroot)) => + Ok(ActiveLock { lockscope, locktype, depth, owner, timeout, locktoken, lockroot }), + _ => Err(ParsingError::MissingChild), + } + } +} + +impl QRead for Depth { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "depth").await?; + let depth_str = xml.tag_string().await?; + xml.close().await?; + match depth_str.as_str() { + "0" => Ok(Depth::Zero), + "1" => Ok(Depth::One), + "infinity" => Ok(Depth::Infinity), + _ => Err(ParsingError::WrongToken), + } + } +} + +impl QRead for Owner { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "owner").await?; + + let mut owner = Owner::Unknown; + loop { + match xml.peek() { + Event::Text(_) | Event::CData(_) => { + let txt = xml.tag_string().await?; + if matches!(owner, Owner::Unknown) { + owner = Owner::Txt(txt); + } + } + Event::Start(_) | Event::Empty(_) => { + match Href::qread(xml).await { + Ok(href) => { owner = Owner::Href(href); }, + Err(ParsingError::Recoverable) => { xml.skip().await?; }, + Err(e) => return Err(e), + } + } + Event::End(_) => break, + _ => { xml.skip().await?; }, + } + }; + xml.close().await?; + Ok(owner) + } +} + +impl QRead for Timeout { + async fn qread(xml: &mut Reader) -> Result { + const SEC_PFX: &str = "SEC_PFX"; + xml.open(DAV_URN, "timeout").await?; + + let timeout = match xml.tag_string().await?.as_str() { + "Infinite" => Timeout::Infinite, + seconds => match seconds.strip_prefix(SEC_PFX) { + Some(secs) => Timeout::Seconds(secs.parse::()?), + None => return Err(ParsingError::InvalidValue), + }, + }; + + xml.close().await?; + Ok(timeout) + } +} + +impl QRead for LockToken { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "locktoken").await?; + let href = Href::qread(xml).await?; + xml.close().await?; + Ok(LockToken(href)) + } +} + +impl QRead for LockRoot { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "lockroot").await?; + let href = Href::qread(xml).await?; + xml.close().await?; + Ok(LockRoot(href)) + } +} + +impl QRead> for ResourceType { + async fn qread(xml: &mut Reader) -> Result { + if xml.maybe_open(DAV_URN, "collection").await?.is_some() { + xml.close().await?; + return Ok(ResourceType::Collection) + } + + E::ResourceType::qread(xml).await.map(ResourceType::Extension) + } +} + +impl QRead for LockEntry { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "lockentry").await?; + let (mut maybe_scope, mut maybe_type) = (None, None); + + loop { + let mut dirty = false; + xml.maybe_read::(&mut maybe_scope, &mut dirty).await?; + xml.maybe_read::(&mut maybe_type, &mut dirty).await?; + if !dirty { + match xml.peek() { + Event::End(_) => break, + _ => xml.skip().await?, + }; + } + } + + xml.close().await?; + match (maybe_scope, maybe_type) { + (Some(lockscope), Some(locktype)) => Ok(LockEntry { lockscope, locktype }), + _ => Err(ParsingError::MissingChild), + } + } +} + +impl QRead for LockScope { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "lockscope").await?; + + let lockscope = loop { + if xml.maybe_open(DAV_URN, "exclusive").await?.is_some() { + xml.close().await?; + break LockScope::Exclusive + } else if xml.maybe_open(DAV_URN, "shared").await?.is_some() { + xml.close().await?; + break LockScope::Shared + } + + xml.skip().await?; + }; + + xml.close().await?; + Ok(lockscope) + } +} + +impl QRead for LockType { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "locktype").await?; + + let locktype = loop { + if xml.maybe_open(DAV_URN, "write").await?.is_some() { + xml.close().await?; + break LockType::Write + } + + xml.skip().await?; + }; + + xml.close().await?; + Ok(locktype) + } +} + +impl QRead for Href { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "href").await?; + let mut url = xml.tag_string().await?; + xml.close().await?; + Ok(Href(url)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use chrono::{FixedOffset, DateTime, TimeZone, Utc}; + use crate::realization::Core; + + #[tokio::test] + async fn basic_propfind_propname() { + let src = r#" + + + + + +"#; + + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); + let got = rdr.find::>().await.unwrap(); + + assert_eq!(got, PropFind::::PropName); + } + + #[tokio::test] + async fn basic_propfind_prop() { + let src = r#" + + + + + + + + + + + + + +"#; + + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); + let got = rdr.find::>().await.unwrap(); + + assert_eq!(got, PropFind::Prop(PropName(vec![ + PropertyRequest::DisplayName, + PropertyRequest::GetContentLength, + PropertyRequest::GetContentType, + PropertyRequest::GetEtag, + PropertyRequest::GetLastModified, + PropertyRequest::ResourceType, + PropertyRequest::SupportedLock, + ]))); + } + + #[tokio::test] + async fn rfc_lock_error() { + let src = r#" + + + /locked/ + + "#; + + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); + let got = rdr.find::>().await.unwrap(); + + assert_eq!(got, Error(vec![ + Violation::LockTokenSubmitted(vec![ + Href("/locked/".into()) + ]) + ])); + } + + + #[tokio::test] + async fn rfc_propertyupdate() { + let src = r#" + + + + + Jim Whitehead + Roy Fielding + + + + + + + "#; + + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); + let got = rdr.find::>().await.unwrap(); + + assert_eq!(got, PropertyUpdate(vec![ + PropertyUpdateItem::Set(Set(PropValue(vec![]))), + PropertyUpdateItem::Remove(Remove(PropName(vec![]))), + ])); + } + + #[tokio::test] + async fn rfc_lockinfo() { + let src = r#" + + + + + + http://example.org/~ejw/contact.html + + +"#; + + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); + let got = rdr.find::().await.unwrap(); + + assert_eq!(got, LockInfo { + lockscope: LockScope::Exclusive, + locktype: LockType::Write, + owner: Some(Owner::Href(Href("http://example.org/~ejw/contact.html".into()))), + }); + } + + #[tokio::test] + async fn rfc_multistatus_name() { + let src = r#" + + + + http://www.example.com/container/ + + + + + + + + + + HTTP/1.1 200 OK + + + + http://www.example.com/container/front.html + + + + + + + + + + + + + HTTP/1.1 200 OK + + + +"#; + + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); + let got = rdr.find::>>().await.unwrap(); + + assert_eq!(got, Multistatus { + responses: vec![ + Response { + status_or_propstat: StatusOrPropstat::PropStat( + Href("http://www.example.com/container/".into()), + vec![PropStat { + prop: PropName(vec![ + PropertyRequest::CreationDate, + PropertyRequest::DisplayName, + PropertyRequest::ResourceType, + PropertyRequest::SupportedLock, + ]), + status: Status(http::status::StatusCode::OK), + error: None, + responsedescription: None, + }], + ), + error: None, + responsedescription: None, + location: None, + }, + Response { + status_or_propstat: StatusOrPropstat::PropStat( + Href("http://www.example.com/container/front.html".into()), + vec![PropStat { + prop: PropName(vec![ + PropertyRequest::CreationDate, + PropertyRequest::DisplayName, + PropertyRequest::GetContentLength, + PropertyRequest::GetContentType, + PropertyRequest::GetEtag, + PropertyRequest::GetLastModified, + PropertyRequest::ResourceType, + PropertyRequest::SupportedLock, + ]), + status: Status(http::status::StatusCode::OK), + error: None, + responsedescription: None, + }], + ), + error: None, + responsedescription: None, + location: None, + }, + ], + responsedescription: None, + }); + } + + + #[tokio::test] + async fn rfc_multistatus_value() { + let src = r#" + + + + /container/ + + + Box type A + Hadrian + 1997-12-01T17:42:21-08:00 + Example collection + + + + + + + + + + + + + HTTP/1.1 200 OK + + + + /container/front.html + + + Box type B + + 1997-12-01T18:27:21-08:00 + Example HTML resource + 4525 + text/html + "zzyzx" + Mon, 12 Jan 1998 09:25:56 GMT + + + + + + + + + + + + + HTTP/1.1 200 OK + + + "#; + + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); + let got = rdr.find::>>().await.unwrap(); + + assert_eq!(got, Multistatus { + responses: vec![ + Response { + status_or_propstat: StatusOrPropstat::PropStat( + Href("/container/".into()), + vec![PropStat { + prop: PropValue(vec![ + Property::CreationDate(FixedOffset::west_opt(8 * 3600).unwrap().with_ymd_and_hms(1997, 12, 01, 17, 42, 21).unwrap()), + Property::DisplayName("Example collection".into()), + Property::ResourceType(vec![ResourceType::Collection]), + Property::SupportedLock(vec![ + LockEntry { + lockscope: LockScope::Exclusive, + locktype: LockType::Write, + }, + LockEntry { + lockscope: LockScope::Shared, + locktype: LockType::Write, + }, + ]), + ]), + status: Status(http::status::StatusCode::OK), + error: None, + responsedescription: None, + }], + ), + error: None, + responsedescription: None, + location: None, + + }, + Response { + status_or_propstat: StatusOrPropstat::PropStat( + Href("/container/front.html".into()), + vec![PropStat { + prop: PropValue(vec![ + Property::CreationDate(FixedOffset::west_opt(8 * 3600).unwrap().with_ymd_and_hms(1997, 12, 01, 18, 27, 21).unwrap()), + Property::DisplayName("Example HTML resource".into()), + Property::GetContentLength(4525), + Property::GetContentType("text/html".into()), + Property::GetEtag(r#""zzyzx""#.into()), + Property::GetLastModified(FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(1998, 01, 12, 09, 25, 56).unwrap()), + //Property::ResourceType(vec![]), + Property::SupportedLock(vec![ + LockEntry { + lockscope: LockScope::Exclusive, + locktype: LockType::Write, + }, + LockEntry { + lockscope: LockScope::Shared, + locktype: LockType::Write, + }, + ]), + ]), + status: Status(http::status::StatusCode::OK), + error: None, + responsedescription: None, + }], + ), + error: None, + responsedescription: None, + location: None, + + }, + ], + responsedescription: None, + }); + } + +} -- cgit v1.2.3 From 11462f80c4ae25696c7436ed7aacb92074d7e911 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Fri, 8 Mar 2024 09:55:33 +0100 Subject: Re-enable proto --- aero-dav/src/decoder.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'aero-dav/src/decoder.rs') diff --git a/aero-dav/src/decoder.rs b/aero-dav/src/decoder.rs index 65cb712..02bc376 100644 --- a/aero-dav/src/decoder.rs +++ b/aero-dav/src/decoder.rs @@ -1,14 +1,9 @@ -use std::future::Future; - use quick_xml::events::Event; -use quick_xml::events::attributes::AttrError; -use quick_xml::name::{Namespace, QName, PrefixDeclaration, ResolveResult, ResolveResult::*}; -use quick_xml::reader::NsReader; -use tokio::io::AsyncBufRead; +use chrono::DateTime; use super::types::*; use super::error::ParsingError; -use super::xml::{Node, QRead, Reader, IRead, DAV_URN, CAL_URN}; +use super::xml::{Node, QRead, Reader, IRead, DAV_URN}; //@TODO (1) Rewrite all objects as Href, // where we return Ok(None) instead of trying to find the object at any cost. @@ -119,7 +114,7 @@ impl QRead for LockInfo { impl QRead> for PropValue { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "prop").await?; - let mut acc = xml.collect::>().await?; + let acc = xml.collect::>().await?; xml.close().await?; Ok(PropValue(acc)) } @@ -352,8 +347,6 @@ impl QRead> for PropertyRequest { impl QRead> for Property { async fn qread(xml: &mut Reader) -> Result { - use chrono::{DateTime, FixedOffset, TimeZone}; - // Core WebDAV properties if xml.maybe_open(DAV_URN, "creationdate").await?.is_some() { let datestr = xml.tag_string().await?; @@ -592,7 +585,7 @@ impl QRead for LockType { impl QRead for Href { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "href").await?; - let mut url = xml.tag_string().await?; + let url = xml.tag_string().await?; xml.close().await?; Ok(Href(url)) } -- cgit v1.2.3 From 4d65366ff368cc9ea35115cb7e701bfebb166bc6 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Fri, 8 Mar 2024 11:34:24 +0100 Subject: Fixed some parsing bugs --- aero-dav/src/decoder.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'aero-dav/src/decoder.rs') diff --git a/aero-dav/src/decoder.rs b/aero-dav/src/decoder.rs index 02bc376..28442a6 100644 --- a/aero-dav/src/decoder.rs +++ b/aero-dav/src/decoder.rs @@ -23,8 +23,8 @@ impl QRead> for PropFind { let propfind: PropFind = loop { // allprop if let Some(_) = xml.maybe_open(DAV_URN, "allprop").await? { - let includ = xml.maybe_find::>().await?; xml.close().await?; + let includ = xml.maybe_find::>().await?; break PropFind::AllProp(includ) } @@ -594,8 +594,9 @@ impl QRead for Href { #[cfg(test)] mod tests { use super::*; - use chrono::{FixedOffset, DateTime, TimeZone, Utc}; + use chrono::{FixedOffset, TimeZone}; use crate::realization::Core; + use quick_xml::reader::NsReader; #[tokio::test] async fn basic_propfind_propname() { @@ -910,7 +911,7 @@ mod tests { Property::GetContentType("text/html".into()), Property::GetEtag(r#""zzyzx""#.into()), Property::GetLastModified(FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(1998, 01, 12, 09, 25, 56).unwrap()), - //Property::ResourceType(vec![]), + Property::ResourceType(vec![]), Property::SupportedLock(vec![ LockEntry { lockscope: LockScope::Exclusive, -- cgit v1.2.3 From b786573e08c78b672880cd212db45fc58ab82c4c Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Fri, 8 Mar 2024 11:42:44 +0100 Subject: Fixed 2 more bugs --- aero-dav/src/decoder.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'aero-dav/src/decoder.rs') diff --git a/aero-dav/src/decoder.rs b/aero-dav/src/decoder.rs index 28442a6..766d19c 100644 --- a/aero-dav/src/decoder.rs +++ b/aero-dav/src/decoder.rs @@ -473,7 +473,7 @@ impl QRead for Owner { impl QRead for Timeout { async fn qread(xml: &mut Reader) -> Result { - const SEC_PFX: &str = "SEC_PFX"; + const SEC_PFX: &str = "Second-"; xml.open(DAV_URN, "timeout").await?; let timeout = match xml.tag_string().await?.as_str() { @@ -492,7 +492,7 @@ impl QRead for Timeout { impl QRead for LockToken { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "locktoken").await?; - let href = Href::qread(xml).await?; + let href = xml.find::().await?; xml.close().await?; Ok(LockToken(href)) } @@ -501,7 +501,7 @@ impl QRead for LockToken { impl QRead for LockRoot { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "lockroot").await?; - let href = Href::qread(xml).await?; + let href = xml.find::().await?; xml.close().await?; Ok(LockRoot(href)) } -- cgit v1.2.3 From 7459f50b5486a137bc90b7e6e04e915d82230e28 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Fri, 8 Mar 2024 18:23:23 +0100 Subject: WIP implem cal decoder --- aero-dav/src/decoder.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'aero-dav/src/decoder.rs') diff --git a/aero-dav/src/decoder.rs b/aero-dav/src/decoder.rs index 766d19c..de04dd4 100644 --- a/aero-dav/src/decoder.rs +++ b/aero-dav/src/decoder.rs @@ -551,7 +551,9 @@ impl QRead for LockScope { if xml.maybe_open(DAV_URN, "exclusive").await?.is_some() { xml.close().await?; break LockScope::Exclusive - } else if xml.maybe_open(DAV_URN, "shared").await?.is_some() { + } + + if xml.maybe_open(DAV_URN, "shared").await?.is_some() { xml.close().await?; break LockScope::Shared } -- cgit v1.2.3 From d0c47b93fe19a9ebc35d624b9dbed7d1d539ecaa Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Mon, 18 Mar 2024 20:45:30 +0100 Subject: Rework webdav types --- aero-dav/src/decoder.rs | 122 ++++++++++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 50 deletions(-) (limited to 'aero-dav/src/decoder.rs') diff --git a/aero-dav/src/decoder.rs b/aero-dav/src/decoder.rs index de04dd4..bb8d9de 100644 --- a/aero-dav/src/decoder.rs +++ b/aero-dav/src/decoder.rs @@ -12,7 +12,7 @@ use super::xml::{Node, QRead, Reader, IRead, DAV_URN}; // (2) Rewrite QRead and replace Result, _> with Result<_, _>, not found being a possible // error. // (3) Rewrite vectors with xml.collect() -> Result, _> -// (4) Something for alternatives would be great but no idea yet +// (4) Something for alternatives like xml::choices on some lib would be great but no idea yet // ---- ROOT ---- @@ -61,7 +61,7 @@ impl QRead> for PropertyUpdate { } /// Generic response -impl> QRead> for Multistatus { +impl QRead> for Multistatus { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "multistatus").await?; let mut responses = Vec::new(); @@ -113,6 +113,7 @@ impl QRead for LockInfo { // LOCK RESPONSE impl QRead> for PropValue { async fn qread(xml: &mut Reader) -> Result { + println!("---- propvalue"); xml.open(DAV_URN, "prop").await?; let acc = xml.collect::>().await?; xml.close().await?; @@ -134,7 +135,7 @@ impl QRead> for Error { // ---- INNER XML -impl> QRead> for Response { +impl QRead> for Response { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "response").await?; let (mut status, mut error, mut responsedescription, mut location) = (None, None, None, None); @@ -145,7 +146,7 @@ impl> QRead> for Response { let mut dirty = false; xml.maybe_read::(&mut status, &mut dirty).await?; xml.maybe_push::(&mut href, &mut dirty).await?; - xml.maybe_push::>(&mut propstat, &mut dirty).await?; + xml.maybe_push::>(&mut propstat, &mut dirty).await?; xml.maybe_read::>(&mut error, &mut dirty).await?; xml.maybe_read::(&mut responsedescription, &mut dirty).await?; xml.maybe_read::(&mut location, &mut dirty).await?; @@ -174,15 +175,15 @@ impl> QRead> for Response { } } -impl> QRead> for PropStat { +impl QRead> for PropStat { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "propstat").await?; - let (mut m_prop, mut m_status, mut error, mut responsedescription) = (None, None, None, None); + let (mut m_any_prop, mut m_status, mut error, mut responsedescription) = (None, None, None, None); loop { let mut dirty = false; - xml.maybe_read::(&mut m_prop, &mut dirty).await?; + xml.maybe_read::>(&mut m_any_prop, &mut dirty).await?; xml.maybe_read::(&mut m_status, &mut dirty).await?; xml.maybe_read::>(&mut error, &mut dirty).await?; xml.maybe_read::(&mut responsedescription, &mut dirty).await?; @@ -196,7 +197,7 @@ impl> QRead> for PropStat { } xml.close().await?; - match (m_prop, m_status) { + match (m_any_prop, m_status) { (Some(prop), Some(status)) => Ok(PropStat { prop, status, error, responsedescription }), _ => Err(ParsingError::MissingChild), } @@ -309,6 +310,25 @@ impl QRead> for PropName { } } +impl QRead> for AnyProp { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "prop").await?; + let acc = xml.collect::>().await?; + xml.close().await?; + Ok(AnyProp(acc)) + } +} + +impl QRead> for AnyProperty { + async fn qread(xml: &mut Reader) -> Result { + match Property::qread(xml).await { + Err(ParsingError::Recoverable) => (), + otherwise => return otherwise.map(Self::Value) + } + PropertyRequest::qread(xml).await.map(Self::Request) + } +} + impl QRead> for PropertyRequest { async fn qread(xml: &mut Reader) -> Result { let maybe = if xml.maybe_open(DAV_URN, "creationdate").await?.is_some() { @@ -348,43 +368,43 @@ impl QRead> for PropertyRequest { impl QRead> for Property { async fn qread(xml: &mut Reader) -> Result { // Core WebDAV properties - if xml.maybe_open(DAV_URN, "creationdate").await?.is_some() { + if xml.maybe_open_start(DAV_URN, "creationdate").await?.is_some() { let datestr = xml.tag_string().await?; xml.close().await?; return Ok(Property::CreationDate(DateTime::parse_from_rfc3339(datestr.as_str())?)) - } else if xml.maybe_open(DAV_URN, "displayname").await?.is_some() { + } else if xml.maybe_open_start(DAV_URN, "displayname").await?.is_some() { let name = xml.tag_string().await?; xml.close().await?; return Ok(Property::DisplayName(name)) - } else if xml.maybe_open(DAV_URN, "getcontentlanguage").await?.is_some() { + } else if xml.maybe_open_start(DAV_URN, "getcontentlanguage").await?.is_some() { let lang = xml.tag_string().await?; xml.close().await?; return Ok(Property::GetContentLanguage(lang)) - } else if xml.maybe_open(DAV_URN, "getcontentlength").await?.is_some() { + } else if xml.maybe_open_start(DAV_URN, "getcontentlength").await?.is_some() { let cl = xml.tag_string().await?.parse::()?; xml.close().await?; return Ok(Property::GetContentLength(cl)) - } else if xml.maybe_open(DAV_URN, "getcontenttype").await?.is_some() { + } else if xml.maybe_open_start(DAV_URN, "getcontenttype").await?.is_some() { let ct = xml.tag_string().await?; xml.close().await?; return Ok(Property::GetContentType(ct)) - } else if xml.maybe_open(DAV_URN, "getetag").await?.is_some() { + } else if xml.maybe_open_start(DAV_URN, "getetag").await?.is_some() { let etag = xml.tag_string().await?; xml.close().await?; return Ok(Property::GetEtag(etag)) - } else if xml.maybe_open(DAV_URN, "getlastmodified").await?.is_some() { + } else if xml.maybe_open_start(DAV_URN, "getlastmodified").await?.is_some() { let datestr = xml.tag_string().await?; xml.close().await?; return Ok(Property::GetLastModified(DateTime::parse_from_rfc2822(datestr.as_str())?)) - } else if xml.maybe_open(DAV_URN, "lockdiscovery").await?.is_some() { + } else if xml.maybe_open_start(DAV_URN, "lockdiscovery").await?.is_some() { let acc = xml.collect::().await?; xml.close().await?; return Ok(Property::LockDiscovery(acc)) - } else if xml.maybe_open(DAV_URN, "resourcetype").await?.is_some() { + } else if xml.maybe_open_start(DAV_URN, "resourcetype").await?.is_some() { let acc = xml.collect::>().await?; xml.close().await?; return Ok(Property::ResourceType(acc)) - } else if xml.maybe_open(DAV_URN, "supportedlock").await?.is_some() { + } else if xml.maybe_open_start(DAV_URN, "supportedlock").await?.is_some() { let acc = xml.collect::().await?; xml.close().await?; return Ok(Property::SupportedLock(acc)) @@ -758,7 +778,7 @@ mod tests { "#; let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); - let got = rdr.find::>>().await.unwrap(); + let got = rdr.find::>().await.unwrap(); assert_eq!(got, Multistatus { responses: vec![ @@ -766,11 +786,11 @@ mod tests { status_or_propstat: StatusOrPropstat::PropStat( Href("http://www.example.com/container/".into()), vec![PropStat { - prop: PropName(vec![ - PropertyRequest::CreationDate, - PropertyRequest::DisplayName, - PropertyRequest::ResourceType, - PropertyRequest::SupportedLock, + prop: AnyProp(vec![ + AnyProperty::Request(PropertyRequest::CreationDate), + AnyProperty::Request(PropertyRequest::DisplayName), + AnyProperty::Request(PropertyRequest::ResourceType), + AnyProperty::Request(PropertyRequest::SupportedLock), ]), status: Status(http::status::StatusCode::OK), error: None, @@ -785,15 +805,15 @@ mod tests { status_or_propstat: StatusOrPropstat::PropStat( Href("http://www.example.com/container/front.html".into()), vec![PropStat { - prop: PropName(vec![ - PropertyRequest::CreationDate, - PropertyRequest::DisplayName, - PropertyRequest::GetContentLength, - PropertyRequest::GetContentType, - PropertyRequest::GetEtag, - PropertyRequest::GetLastModified, - PropertyRequest::ResourceType, - PropertyRequest::SupportedLock, + prop: AnyProp(vec![ + AnyProperty::Request(PropertyRequest::CreationDate), + AnyProperty::Request(PropertyRequest::DisplayName), + AnyProperty::Request(PropertyRequest::GetContentLength), + AnyProperty::Request(PropertyRequest::GetContentType), + AnyProperty::Request(PropertyRequest::GetEtag), + AnyProperty::Request(PropertyRequest::GetLastModified), + AnyProperty::Request(PropertyRequest::ResourceType), + AnyProperty::Request(PropertyRequest::SupportedLock), ]), status: Status(http::status::StatusCode::OK), error: None, @@ -869,7 +889,7 @@ mod tests { "#; let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); - let got = rdr.find::>>().await.unwrap(); + let got = rdr.find::>().await.unwrap(); assert_eq!(got, Multistatus { responses: vec![ @@ -877,11 +897,11 @@ mod tests { status_or_propstat: StatusOrPropstat::PropStat( Href("/container/".into()), vec![PropStat { - prop: PropValue(vec![ - Property::CreationDate(FixedOffset::west_opt(8 * 3600).unwrap().with_ymd_and_hms(1997, 12, 01, 17, 42, 21).unwrap()), - Property::DisplayName("Example collection".into()), - Property::ResourceType(vec![ResourceType::Collection]), - Property::SupportedLock(vec![ + prop: AnyProp(vec![ + AnyProperty::Value(Property::CreationDate(FixedOffset::west_opt(8 * 3600).unwrap().with_ymd_and_hms(1997, 12, 01, 17, 42, 21).unwrap())), + AnyProperty::Value(Property::DisplayName("Example collection".into())), + AnyProperty::Value(Property::ResourceType(vec![ResourceType::Collection])), + AnyProperty::Value(Property::SupportedLock(vec![ LockEntry { lockscope: LockScope::Exclusive, locktype: LockType::Write, @@ -890,7 +910,7 @@ mod tests { lockscope: LockScope::Shared, locktype: LockType::Write, }, - ]), + ])), ]), status: Status(http::status::StatusCode::OK), error: None, @@ -906,15 +926,17 @@ mod tests { status_or_propstat: StatusOrPropstat::PropStat( Href("/container/front.html".into()), vec![PropStat { - prop: PropValue(vec![ - Property::CreationDate(FixedOffset::west_opt(8 * 3600).unwrap().with_ymd_and_hms(1997, 12, 01, 18, 27, 21).unwrap()), - Property::DisplayName("Example HTML resource".into()), - Property::GetContentLength(4525), - Property::GetContentType("text/html".into()), - Property::GetEtag(r#""zzyzx""#.into()), - Property::GetLastModified(FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(1998, 01, 12, 09, 25, 56).unwrap()), - Property::ResourceType(vec![]), - Property::SupportedLock(vec![ + prop: AnyProp(vec![ + AnyProperty::Value(Property::CreationDate(FixedOffset::west_opt(8 * 3600).unwrap().with_ymd_and_hms(1997, 12, 01, 18, 27, 21).unwrap())), + AnyProperty::Value(Property::DisplayName("Example HTML resource".into())), + AnyProperty::Value(Property::GetContentLength(4525)), + AnyProperty::Value(Property::GetContentType("text/html".into())), + AnyProperty::Value(Property::GetEtag(r#""zzyzx""#.into())), + AnyProperty::Value(Property::GetLastModified(FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(1998, 01, 12, 09, 25, 56).unwrap())), + //@FIXME know bug, can't disambiguate between an empty resource + //type value and a request resource type + AnyProperty::Request(PropertyRequest::ResourceType), + AnyProperty::Value(Property::SupportedLock(vec![ LockEntry { lockscope: LockScope::Exclusive, locktype: LockType::Write, @@ -923,7 +945,7 @@ mod tests { lockscope: LockScope::Shared, locktype: LockType::Write, }, - ]), + ])), ]), status: Status(http::status::StatusCode::OK), error: None, -- cgit v1.2.3 From 32dfd25f570b7a55bf43752684d286be0f6b2dc2 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Thu, 16 May 2024 17:38:34 +0200 Subject: format + WIP calendar-query --- aero-dav/src/decoder.rs | 674 ++++++++++++++++++++++++++++++------------------ 1 file changed, 428 insertions(+), 246 deletions(-) (limited to 'aero-dav/src/decoder.rs') diff --git a/aero-dav/src/decoder.rs b/aero-dav/src/decoder.rs index bb8d9de..bd724e8 100644 --- a/aero-dav/src/decoder.rs +++ b/aero-dav/src/decoder.rs @@ -1,9 +1,9 @@ -use quick_xml::events::Event; use chrono::DateTime; +use quick_xml::events::Event; -use super::types::*; use super::error::ParsingError; -use super::xml::{Node, QRead, Reader, IRead, DAV_URN}; +use super::types::*; +use super::xml::{IRead, Node, QRead, Reader, DAV_URN}; //@TODO (1) Rewrite all objects as Href, // where we return Ok(None) instead of trying to find the object at any cost. @@ -25,20 +25,21 @@ impl QRead> for PropFind { if let Some(_) = xml.maybe_open(DAV_URN, "allprop").await? { xml.close().await?; let includ = xml.maybe_find::>().await?; - break PropFind::AllProp(includ) + break PropFind::AllProp(includ); } // propname if let Some(_) = xml.maybe_open(DAV_URN, "propname").await? { xml.close().await?; - break PropFind::PropName + break PropFind::PropName; } // prop let (mut maybe_prop, mut dirty) = (None, false); - xml.maybe_read::>(&mut maybe_prop, &mut dirty).await?; + xml.maybe_read::>(&mut maybe_prop, &mut dirty) + .await?; if let Some(prop) = maybe_prop { - break PropFind::Prop(prop) + break PropFind::Prop(prop); } // not found, skipping @@ -80,7 +81,10 @@ impl QRead> for Multistatus { } xml.close().await?; - Ok(Multistatus { responses, responsedescription }) + Ok(Multistatus { + responses, + responsedescription, + }) } } @@ -91,7 +95,8 @@ impl QRead for LockInfo { let (mut m_scope, mut m_type, mut owner) = (None, None, None); loop { let mut dirty = false; - xml.maybe_read::(&mut m_scope, &mut dirty).await?; + xml.maybe_read::(&mut m_scope, &mut dirty) + .await?; xml.maybe_read::(&mut m_type, &mut dirty).await?; xml.maybe_read::(&mut owner, &mut dirty).await?; @@ -104,7 +109,11 @@ impl QRead for LockInfo { } xml.close().await?; match (m_scope, m_type) { - (Some(lockscope), Some(locktype)) => Ok(LockInfo { lockscope, locktype, owner }), + (Some(lockscope), Some(locktype)) => Ok(LockInfo { + lockscope, + locktype, + owner, + }), _ => Err(ParsingError::MissingChild), } } @@ -121,7 +130,6 @@ impl QRead> for PropValue { } } - /// Error response impl QRead> for Error { async fn qread(xml: &mut Reader) -> Result { @@ -132,13 +140,12 @@ impl QRead> for Error { } } - - // ---- INNER XML impl QRead> for Response { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "response").await?; - let (mut status, mut error, mut responsedescription, mut location) = (None, None, None, None); + let (mut status, mut error, mut responsedescription, mut location) = + (None, None, None, None); let mut href = Vec::new(); let mut propstat = Vec::new(); @@ -146,28 +153,38 @@ impl QRead> for Response { let mut dirty = false; xml.maybe_read::(&mut status, &mut dirty).await?; xml.maybe_push::(&mut href, &mut dirty).await?; - xml.maybe_push::>(&mut propstat, &mut dirty).await?; + xml.maybe_push::>(&mut propstat, &mut dirty) + .await?; xml.maybe_read::>(&mut error, &mut dirty).await?; - xml.maybe_read::(&mut responsedescription, &mut dirty).await?; - xml.maybe_read::(&mut location, &mut dirty).await?; + xml.maybe_read::(&mut responsedescription, &mut dirty) + .await?; + xml.maybe_read::(&mut location, &mut dirty) + .await?; if !dirty { match xml.peek() { Event::End(_) => break, - _ => { xml.skip().await? }, + _ => xml.skip().await?, }; } } xml.close().await?; match (status, &propstat[..], &href[..]) { - (Some(status), &[], &[_, ..]) => Ok(Response { - status_or_propstat: StatusOrPropstat::Status(href, status), - error, responsedescription, location, + (Some(status), &[], &[_, ..]) => Ok(Response { + status_or_propstat: StatusOrPropstat::Status(href, status), + error, + responsedescription, + location, }), (None, &[_, ..], &[_, ..]) => Ok(Response { - status_or_propstat: StatusOrPropstat::PropStat(href.into_iter().next().unwrap(), propstat), - error, responsedescription, location, + status_or_propstat: StatusOrPropstat::PropStat( + href.into_iter().next().unwrap(), + propstat, + ), + error, + responsedescription, + location, }), (Some(_), &[_, ..], _) => Err(ParsingError::InvalidValue), _ => Err(ParsingError::MissingChild), @@ -179,14 +196,17 @@ impl QRead> for PropStat { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "propstat").await?; - let (mut m_any_prop, mut m_status, mut error, mut responsedescription) = (None, None, None, None); + let (mut m_any_prop, mut m_status, mut error, mut responsedescription) = + (None, None, None, None); loop { let mut dirty = false; - xml.maybe_read::>(&mut m_any_prop, &mut dirty).await?; + xml.maybe_read::>(&mut m_any_prop, &mut dirty) + .await?; xml.maybe_read::(&mut m_status, &mut dirty).await?; xml.maybe_read::>(&mut error, &mut dirty).await?; - xml.maybe_read::(&mut responsedescription, &mut dirty).await?; + xml.maybe_read::(&mut responsedescription, &mut dirty) + .await?; if !dirty { match xml.peek() { @@ -198,7 +218,12 @@ impl QRead> for PropStat { xml.close().await?; match (m_any_prop, m_status) { - (Some(prop), Some(status)) => Ok(PropStat { prop, status, error, responsedescription }), + (Some(prop), Some(status)) => Ok(PropStat { + prop, + status, + error, + responsedescription, + }), _ => Err(ParsingError::MissingChild), } } @@ -208,8 +233,12 @@ impl QRead for Status { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "status").await?; 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))?; + 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.close().await?; Ok(Status(code)) } @@ -263,27 +292,55 @@ impl QRead> for Set { impl QRead> for Violation { async fn qread(xml: &mut Reader) -> Result { - if xml.maybe_open(DAV_URN, "lock-token-matches-request-uri").await?.is_some() { + if xml + .maybe_open(DAV_URN, "lock-token-matches-request-uri") + .await? + .is_some() + { xml.close().await?; Ok(Violation::LockTokenMatchesRequestUri) - } else if xml.maybe_open(DAV_URN, "lock-token-submitted").await?.is_some() { + } else if xml + .maybe_open(DAV_URN, "lock-token-submitted") + .await? + .is_some() + { let links = xml.collect::().await?; xml.close().await?; Ok(Violation::LockTokenSubmitted(links)) - } else if xml.maybe_open(DAV_URN, "no-conflicting-lock").await?.is_some() { + } else if xml + .maybe_open(DAV_URN, "no-conflicting-lock") + .await? + .is_some() + { let links = xml.collect::().await?; xml.close().await?; Ok(Violation::NoConflictingLock(links)) - } else if xml.maybe_open(DAV_URN, "no-external-entities").await?.is_some() { + } else if xml + .maybe_open(DAV_URN, "no-external-entities") + .await? + .is_some() + { xml.close().await?; Ok(Violation::NoExternalEntities) - } else if xml.maybe_open(DAV_URN, "preserved-live-properties").await?.is_some() { + } else if xml + .maybe_open(DAV_URN, "preserved-live-properties") + .await? + .is_some() + { xml.close().await?; Ok(Violation::PreservedLiveProperties) - } else if xml.maybe_open(DAV_URN, "propfind-finite-depth").await?.is_some() { + } else if xml + .maybe_open(DAV_URN, "propfind-finite-depth") + .await? + .is_some() + { xml.close().await?; Ok(Violation::PropfindFiniteDepth) - } else if xml.maybe_open(DAV_URN, "cannot-modify-protected-property").await?.is_some() { + } else if xml + .maybe_open(DAV_URN, "cannot-modify-protected-property") + .await? + .is_some() + { xml.close().await?; Ok(Violation::CannotModifyProtectedProperty) } else { @@ -323,7 +380,7 @@ impl QRead> for AnyProperty { async fn qread(xml: &mut Reader) -> Result { match Property::qread(xml).await { Err(ParsingError::Recoverable) => (), - otherwise => return otherwise.map(Self::Value) + otherwise => return otherwise.map(Self::Value), } PropertyRequest::qread(xml).await.map(Self::Request) } @@ -335,7 +392,11 @@ impl QRead> for PropertyRequest { Some(PropertyRequest::CreationDate) } else if xml.maybe_open(DAV_URN, "displayname").await?.is_some() { Some(PropertyRequest::DisplayName) - } else if xml.maybe_open(DAV_URN, "getcontentlanguage").await?.is_some() { + } else if xml + .maybe_open(DAV_URN, "getcontentlanguage") + .await? + .is_some() + { Some(PropertyRequest::GetContentLanguage) } else if xml.maybe_open(DAV_URN, "getcontentlength").await?.is_some() { Some(PropertyRequest::GetContentLength) @@ -359,8 +420,10 @@ impl QRead> for PropertyRequest { Some(pr) => { xml.close().await?; Ok(pr) - }, - None => E::PropertyRequest::qread(xml).await.map(PropertyRequest::Extension), + } + None => E::PropertyRequest::qread(xml) + .await + .map(PropertyRequest::Extension), } } } @@ -368,46 +431,86 @@ impl QRead> for PropertyRequest { impl QRead> for Property { async fn qread(xml: &mut Reader) -> Result { // Core WebDAV properties - if xml.maybe_open_start(DAV_URN, "creationdate").await?.is_some() { + if xml + .maybe_open_start(DAV_URN, "creationdate") + .await? + .is_some() + { let datestr = xml.tag_string().await?; xml.close().await?; - return Ok(Property::CreationDate(DateTime::parse_from_rfc3339(datestr.as_str())?)) - } else if xml.maybe_open_start(DAV_URN, "displayname").await?.is_some() { + return Ok(Property::CreationDate(DateTime::parse_from_rfc3339( + datestr.as_str(), + )?)); + } else if xml + .maybe_open_start(DAV_URN, "displayname") + .await? + .is_some() + { let name = xml.tag_string().await?; xml.close().await?; - return Ok(Property::DisplayName(name)) - } else if xml.maybe_open_start(DAV_URN, "getcontentlanguage").await?.is_some() { + return Ok(Property::DisplayName(name)); + } else if xml + .maybe_open_start(DAV_URN, "getcontentlanguage") + .await? + .is_some() + { let lang = xml.tag_string().await?; xml.close().await?; - return Ok(Property::GetContentLanguage(lang)) - } else if xml.maybe_open_start(DAV_URN, "getcontentlength").await?.is_some() { + return Ok(Property::GetContentLanguage(lang)); + } else if xml + .maybe_open_start(DAV_URN, "getcontentlength") + .await? + .is_some() + { let cl = xml.tag_string().await?.parse::()?; xml.close().await?; - return Ok(Property::GetContentLength(cl)) - } else if xml.maybe_open_start(DAV_URN, "getcontenttype").await?.is_some() { + return Ok(Property::GetContentLength(cl)); + } else if xml + .maybe_open_start(DAV_URN, "getcontenttype") + .await? + .is_some() + { let ct = xml.tag_string().await?; xml.close().await?; - return Ok(Property::GetContentType(ct)) + return Ok(Property::GetContentType(ct)); } else if xml.maybe_open_start(DAV_URN, "getetag").await?.is_some() { let etag = xml.tag_string().await?; xml.close().await?; - return Ok(Property::GetEtag(etag)) - } else if xml.maybe_open_start(DAV_URN, "getlastmodified").await?.is_some() { + return Ok(Property::GetEtag(etag)); + } else if xml + .maybe_open_start(DAV_URN, "getlastmodified") + .await? + .is_some() + { let datestr = xml.tag_string().await?; xml.close().await?; - return Ok(Property::GetLastModified(DateTime::parse_from_rfc2822(datestr.as_str())?)) - } else if xml.maybe_open_start(DAV_URN, "lockdiscovery").await?.is_some() { + return Ok(Property::GetLastModified(DateTime::parse_from_rfc2822( + datestr.as_str(), + )?)); + } else if xml + .maybe_open_start(DAV_URN, "lockdiscovery") + .await? + .is_some() + { let acc = xml.collect::().await?; xml.close().await?; - return Ok(Property::LockDiscovery(acc)) - } else if xml.maybe_open_start(DAV_URN, "resourcetype").await?.is_some() { + return Ok(Property::LockDiscovery(acc)); + } else if xml + .maybe_open_start(DAV_URN, "resourcetype") + .await? + .is_some() + { let acc = xml.collect::>().await?; xml.close().await?; - return Ok(Property::ResourceType(acc)) - } else if xml.maybe_open_start(DAV_URN, "supportedlock").await?.is_some() { + return Ok(Property::ResourceType(acc)); + } else if xml + .maybe_open_start(DAV_URN, "supportedlock") + .await? + .is_some() + { let acc = xml.collect::().await?; xml.close().await?; - return Ok(Property::SupportedLock(acc)) + return Ok(Property::SupportedLock(acc)); } // Option 2: an extension property, delegating @@ -418,31 +521,49 @@ impl QRead> for Property { impl QRead for ActiveLock { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "activelock").await?; - let (mut m_scope, mut m_type, mut m_depth, mut owner, mut timeout, mut locktoken, mut m_root) = - (None, None, None, None, None, None, None); + let ( + mut m_scope, + mut m_type, + mut m_depth, + mut owner, + mut timeout, + mut locktoken, + mut m_root, + ) = (None, None, None, None, None, None, None); loop { let mut dirty = false; - xml.maybe_read::(&mut m_scope, &mut dirty).await?; + xml.maybe_read::(&mut m_scope, &mut dirty) + .await?; xml.maybe_read::(&mut m_type, &mut dirty).await?; xml.maybe_read::(&mut m_depth, &mut dirty).await?; xml.maybe_read::(&mut owner, &mut dirty).await?; xml.maybe_read::(&mut timeout, &mut dirty).await?; - xml.maybe_read::(&mut locktoken, &mut dirty).await?; + xml.maybe_read::(&mut locktoken, &mut dirty) + .await?; xml.maybe_read::(&mut m_root, &mut dirty).await?; if !dirty { match xml.peek() { Event::End(_) => break, - _ => { xml.skip().await?; }, + _ => { + xml.skip().await?; + } } } } xml.close().await?; match (m_scope, m_type, m_depth, m_root) { - (Some(lockscope), Some(locktype), Some(depth), Some(lockroot)) => - Ok(ActiveLock { lockscope, locktype, depth, owner, timeout, locktoken, lockroot }), + (Some(lockscope), Some(locktype), Some(depth), Some(lockroot)) => Ok(ActiveLock { + lockscope, + locktype, + depth, + owner, + timeout, + locktoken, + lockroot, + }), _ => Err(ParsingError::MissingChild), } } @@ -465,7 +586,7 @@ impl QRead for Depth { impl QRead for Owner { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "owner").await?; - + let mut owner = Owner::Unknown; loop { match xml.peek() { @@ -475,17 +596,21 @@ impl QRead for Owner { owner = Owner::Txt(txt); } } - Event::Start(_) | Event::Empty(_) => { - match Href::qread(xml).await { - Ok(href) => { owner = Owner::Href(href); }, - Err(ParsingError::Recoverable) => { xml.skip().await?; }, - Err(e) => return Err(e), + Event::Start(_) | Event::Empty(_) => match Href::qread(xml).await { + Ok(href) => { + owner = Owner::Href(href); } - } + Err(ParsingError::Recoverable) => { + xml.skip().await?; + } + Err(e) => return Err(e), + }, Event::End(_) => break, - _ => { xml.skip().await?; }, + _ => { + xml.skip().await?; + } } - }; + } xml.close().await?; Ok(owner) } @@ -495,7 +620,7 @@ impl QRead for Timeout { async fn qread(xml: &mut Reader) -> Result { const SEC_PFX: &str = "Second-"; xml.open(DAV_URN, "timeout").await?; - + let timeout = match xml.tag_string().await?.as_str() { "Infinite" => Timeout::Infinite, seconds => match seconds.strip_prefix(SEC_PFX) { @@ -531,10 +656,12 @@ impl QRead> for ResourceType { async fn qread(xml: &mut Reader) -> Result { if xml.maybe_open(DAV_URN, "collection").await?.is_some() { xml.close().await?; - return Ok(ResourceType::Collection) + return Ok(ResourceType::Collection); } - - E::ResourceType::qread(xml).await.map(ResourceType::Extension) + + E::ResourceType::qread(xml) + .await + .map(ResourceType::Extension) } } @@ -545,8 +672,10 @@ impl QRead for LockEntry { loop { let mut dirty = false; - xml.maybe_read::(&mut maybe_scope, &mut dirty).await?; - xml.maybe_read::(&mut maybe_type, &mut dirty).await?; + xml.maybe_read::(&mut maybe_scope, &mut dirty) + .await?; + xml.maybe_read::(&mut maybe_type, &mut dirty) + .await?; if !dirty { match xml.peek() { Event::End(_) => break, @@ -557,7 +686,10 @@ impl QRead for LockEntry { xml.close().await?; match (maybe_scope, maybe_type) { - (Some(lockscope), Some(locktype)) => Ok(LockEntry { lockscope, locktype }), + (Some(lockscope), Some(locktype)) => Ok(LockEntry { + lockscope, + locktype, + }), _ => Err(ParsingError::MissingChild), } } @@ -570,12 +702,12 @@ impl QRead for LockScope { let lockscope = loop { if xml.maybe_open(DAV_URN, "exclusive").await?.is_some() { xml.close().await?; - break LockScope::Exclusive - } + break LockScope::Exclusive; + } if xml.maybe_open(DAV_URN, "shared").await?.is_some() { xml.close().await?; - break LockScope::Shared + break LockScope::Shared; } xml.skip().await?; @@ -593,7 +725,7 @@ impl QRead for LockType { let locktype = loop { if xml.maybe_open(DAV_URN, "write").await?.is_some() { xml.close().await?; - break LockType::Write + break LockType::Write; } xml.skip().await?; @@ -616,8 +748,8 @@ impl QRead for Href { #[cfg(test)] mod tests { use super::*; - use chrono::{FixedOffset, TimeZone}; use crate::realization::Core; + use chrono::{FixedOffset, TimeZone}; use quick_xml::reader::NsReader; #[tokio::test] @@ -630,8 +762,10 @@ mod tests { "#; - let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); - let got = rdr.find::>().await.unwrap(); + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())) + .await + .unwrap(); + let got = rdr.find::>().await.unwrap(); assert_eq!(got, PropFind::::PropName); } @@ -654,18 +788,23 @@ mod tests { "#; - let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); - let got = rdr.find::>().await.unwrap(); - - assert_eq!(got, PropFind::Prop(PropName(vec![ - PropertyRequest::DisplayName, - PropertyRequest::GetContentLength, - PropertyRequest::GetContentType, - PropertyRequest::GetEtag, - PropertyRequest::GetLastModified, - PropertyRequest::ResourceType, - PropertyRequest::SupportedLock, - ]))); + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())) + .await + .unwrap(); + let got = rdr.find::>().await.unwrap(); + + assert_eq!( + got, + PropFind::Prop(PropName(vec![ + PropertyRequest::DisplayName, + PropertyRequest::GetContentLength, + PropertyRequest::GetContentType, + PropertyRequest::GetEtag, + PropertyRequest::GetLastModified, + PropertyRequest::ResourceType, + PropertyRequest::SupportedLock, + ])) + ); } #[tokio::test] @@ -677,17 +816,19 @@ mod tests { "#; - let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); - let got = rdr.find::>().await.unwrap(); - - assert_eq!(got, Error(vec![ - Violation::LockTokenSubmitted(vec![ - Href("/locked/".into()) - ]) - ])); + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())) + .await + .unwrap(); + let got = rdr.find::>().await.unwrap(); + + assert_eq!( + got, + Error(vec![Violation::LockTokenSubmitted(vec![Href( + "/locked/".into() + )])]) + ); } - #[tokio::test] async fn rfc_propertyupdate() { let src = r#" @@ -706,13 +847,18 @@ mod tests { "#; - let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); - let got = rdr.find::>().await.unwrap(); + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())) + .await + .unwrap(); + let got = rdr.find::>().await.unwrap(); - assert_eq!(got, PropertyUpdate(vec![ - PropertyUpdateItem::Set(Set(PropValue(vec![]))), - PropertyUpdateItem::Remove(Remove(PropName(vec![]))), - ])); + assert_eq!( + got, + PropertyUpdate(vec![ + PropertyUpdateItem::Set(Set(PropValue(vec![]))), + PropertyUpdateItem::Remove(Remove(PropName(vec![]))), + ]) + ); } #[tokio::test] @@ -728,14 +874,21 @@ mod tests { "#; - let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())) + .await + .unwrap(); let got = rdr.find::().await.unwrap(); - assert_eq!(got, LockInfo { - lockscope: LockScope::Exclusive, - locktype: LockType::Write, - owner: Some(Owner::Href(Href("http://example.org/~ejw/contact.html".into()))), - }); + assert_eq!( + got, + LockInfo { + lockscope: LockScope::Exclusive, + locktype: LockType::Write, + owner: Some(Owner::Href(Href( + "http://example.org/~ejw/contact.html".into() + ))), + } + ); } #[tokio::test] @@ -777,59 +930,63 @@ mod tests { "#; - let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); - let got = rdr.find::>().await.unwrap(); - - assert_eq!(got, Multistatus { - responses: vec![ - Response { - status_or_propstat: StatusOrPropstat::PropStat( - Href("http://www.example.com/container/".into()), - vec![PropStat { - prop: AnyProp(vec![ - AnyProperty::Request(PropertyRequest::CreationDate), - AnyProperty::Request(PropertyRequest::DisplayName), - AnyProperty::Request(PropertyRequest::ResourceType), - AnyProperty::Request(PropertyRequest::SupportedLock), - ]), - status: Status(http::status::StatusCode::OK), - error: None, - responsedescription: None, - }], - ), - error: None, - responsedescription: None, - location: None, - }, - Response { - status_or_propstat: StatusOrPropstat::PropStat( - Href("http://www.example.com/container/front.html".into()), - vec![PropStat { - prop: AnyProp(vec![ - AnyProperty::Request(PropertyRequest::CreationDate), - AnyProperty::Request(PropertyRequest::DisplayName), - AnyProperty::Request(PropertyRequest::GetContentLength), - AnyProperty::Request(PropertyRequest::GetContentType), - AnyProperty::Request(PropertyRequest::GetEtag), - AnyProperty::Request(PropertyRequest::GetLastModified), - AnyProperty::Request(PropertyRequest::ResourceType), - AnyProperty::Request(PropertyRequest::SupportedLock), - ]), - status: Status(http::status::StatusCode::OK), - error: None, - responsedescription: None, - }], - ), - error: None, - responsedescription: None, - location: None, - }, - ], - responsedescription: None, - }); + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())) + .await + .unwrap(); + let got = rdr.find::>().await.unwrap(); + + assert_eq!( + got, + Multistatus { + responses: vec![ + Response { + status_or_propstat: StatusOrPropstat::PropStat( + Href("http://www.example.com/container/".into()), + vec![PropStat { + prop: AnyProp(vec![ + AnyProperty::Request(PropertyRequest::CreationDate), + AnyProperty::Request(PropertyRequest::DisplayName), + AnyProperty::Request(PropertyRequest::ResourceType), + AnyProperty::Request(PropertyRequest::SupportedLock), + ]), + status: Status(http::status::StatusCode::OK), + error: None, + responsedescription: None, + }], + ), + error: None, + responsedescription: None, + location: None, + }, + Response { + status_or_propstat: StatusOrPropstat::PropStat( + Href("http://www.example.com/container/front.html".into()), + vec![PropStat { + prop: AnyProp(vec![ + AnyProperty::Request(PropertyRequest::CreationDate), + AnyProperty::Request(PropertyRequest::DisplayName), + AnyProperty::Request(PropertyRequest::GetContentLength), + AnyProperty::Request(PropertyRequest::GetContentType), + AnyProperty::Request(PropertyRequest::GetEtag), + AnyProperty::Request(PropertyRequest::GetLastModified), + AnyProperty::Request(PropertyRequest::ResourceType), + AnyProperty::Request(PropertyRequest::SupportedLock), + ]), + status: Status(http::status::StatusCode::OK), + error: None, + responsedescription: None, + }], + ), + error: None, + responsedescription: None, + location: None, + }, + ], + responsedescription: None, + } + ); } - #[tokio::test] async fn rfc_multistatus_value() { let src = r#" @@ -888,78 +1045,103 @@ mod tests { "#; - let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); - let got = rdr.find::>().await.unwrap(); - - assert_eq!(got, Multistatus { - responses: vec![ - Response { - status_or_propstat: StatusOrPropstat::PropStat( - Href("/container/".into()), - vec![PropStat { - prop: AnyProp(vec![ - AnyProperty::Value(Property::CreationDate(FixedOffset::west_opt(8 * 3600).unwrap().with_ymd_and_hms(1997, 12, 01, 17, 42, 21).unwrap())), - AnyProperty::Value(Property::DisplayName("Example collection".into())), - AnyProperty::Value(Property::ResourceType(vec![ResourceType::Collection])), - AnyProperty::Value(Property::SupportedLock(vec![ - LockEntry { - lockscope: LockScope::Exclusive, - locktype: LockType::Write, - }, - LockEntry { - lockscope: LockScope::Shared, - locktype: LockType::Write, - }, - ])), - ]), - status: Status(http::status::StatusCode::OK), - error: None, - responsedescription: None, - }], - ), - error: None, - responsedescription: None, - location: None, - - }, - Response { - status_or_propstat: StatusOrPropstat::PropStat( - Href("/container/front.html".into()), - vec![PropStat { - prop: AnyProp(vec![ - AnyProperty::Value(Property::CreationDate(FixedOffset::west_opt(8 * 3600).unwrap().with_ymd_and_hms(1997, 12, 01, 18, 27, 21).unwrap())), - AnyProperty::Value(Property::DisplayName("Example HTML resource".into())), - AnyProperty::Value(Property::GetContentLength(4525)), - AnyProperty::Value(Property::GetContentType("text/html".into())), - AnyProperty::Value(Property::GetEtag(r#""zzyzx""#.into())), - AnyProperty::Value(Property::GetLastModified(FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(1998, 01, 12, 09, 25, 56).unwrap())), - //@FIXME know bug, can't disambiguate between an empty resource - //type value and a request resource type - AnyProperty::Request(PropertyRequest::ResourceType), - AnyProperty::Value(Property::SupportedLock(vec![ - LockEntry { - lockscope: LockScope::Exclusive, - locktype: LockType::Write, - }, - LockEntry { - lockscope: LockScope::Shared, - locktype: LockType::Write, - }, - ])), - ]), - status: Status(http::status::StatusCode::OK), - error: None, - responsedescription: None, - }], - ), - error: None, - responsedescription: None, - location: None, - - }, - ], - responsedescription: None, - }); + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())) + .await + .unwrap(); + let got = rdr.find::>().await.unwrap(); + + assert_eq!( + got, + Multistatus { + responses: vec![ + Response { + status_or_propstat: StatusOrPropstat::PropStat( + Href("/container/".into()), + vec![PropStat { + prop: AnyProp(vec![ + AnyProperty::Value(Property::CreationDate( + FixedOffset::west_opt(8 * 3600) + .unwrap() + .with_ymd_and_hms(1997, 12, 01, 17, 42, 21) + .unwrap() + )), + AnyProperty::Value(Property::DisplayName( + "Example collection".into() + )), + AnyProperty::Value(Property::ResourceType(vec![ + ResourceType::Collection + ])), + AnyProperty::Value(Property::SupportedLock(vec![ + LockEntry { + lockscope: LockScope::Exclusive, + locktype: LockType::Write, + }, + LockEntry { + lockscope: LockScope::Shared, + locktype: LockType::Write, + }, + ])), + ]), + status: Status(http::status::StatusCode::OK), + error: None, + responsedescription: None, + }], + ), + error: None, + responsedescription: None, + location: None, + }, + Response { + status_or_propstat: StatusOrPropstat::PropStat( + Href("/container/front.html".into()), + vec![PropStat { + prop: AnyProp(vec![ + AnyProperty::Value(Property::CreationDate( + FixedOffset::west_opt(8 * 3600) + .unwrap() + .with_ymd_and_hms(1997, 12, 01, 18, 27, 21) + .unwrap() + )), + AnyProperty::Value(Property::DisplayName( + "Example HTML resource".into() + )), + AnyProperty::Value(Property::GetContentLength(4525)), + AnyProperty::Value(Property::GetContentType( + "text/html".into() + )), + AnyProperty::Value(Property::GetEtag(r#""zzyzx""#.into())), + AnyProperty::Value(Property::GetLastModified( + FixedOffset::west_opt(0) + .unwrap() + .with_ymd_and_hms(1998, 01, 12, 09, 25, 56) + .unwrap() + )), + //@FIXME know bug, can't disambiguate between an empty resource + //type value and a request resource type + AnyProperty::Request(PropertyRequest::ResourceType), + AnyProperty::Value(Property::SupportedLock(vec![ + LockEntry { + lockscope: LockScope::Exclusive, + locktype: LockType::Write, + }, + LockEntry { + lockscope: LockScope::Shared, + locktype: LockType::Write, + }, + ])), + ]), + status: Status(http::status::StatusCode::OK), + error: None, + responsedescription: None, + }], + ), + error: None, + responsedescription: None, + location: None, + }, + ], + responsedescription: None, + } + ); } - } -- cgit v1.2.3 From 410d663a5ecbb6ca46ac049227afdaaaf50ac27f Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Tue, 28 May 2024 13:04:46 +0200 Subject: add a multistatus extension entrypoint --- aero-dav/src/decoder.rs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'aero-dav/src/decoder.rs') diff --git a/aero-dav/src/decoder.rs b/aero-dav/src/decoder.rs index bd724e8..bb64455 100644 --- a/aero-dav/src/decoder.rs +++ b/aero-dav/src/decoder.rs @@ -67,11 +67,13 @@ impl QRead> for Multistatus { xml.open(DAV_URN, "multistatus").await?; let mut responses = Vec::new(); let mut responsedescription = None; + let mut extension = None; loop { let mut dirty = false; xml.maybe_push(&mut responses, &mut dirty).await?; xml.maybe_read(&mut responsedescription, &mut dirty).await?; + xml.maybe_read(&mut extension, &mut dirty).await?; if !dirty { match xml.peek() { Event::End(_) => break, @@ -84,6 +86,7 @@ impl QRead> for Multistatus { Ok(Multistatus { responses, responsedescription, + extension, }) } } @@ -983,6 +986,7 @@ mod tests { }, ], responsedescription: None, + extension: None, } ); } @@ -1053,6 +1057,7 @@ mod tests { assert_eq!( got, Multistatus { + extension: None, responses: vec![ Response { status_or_propstat: StatusOrPropstat::PropStat( -- cgit v1.2.3