use super::types::*; use super::xml::{IWrite, Node, QWrite, Writer}; use quick_xml::events::{BytesText, Event}; use quick_xml::Error as QError; // --- XML ROOTS /// PROPFIND REQUEST impl QWrite for PropFind { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("propfind"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; match self { Self::PropName => { let empty_propname = xml.create_dav_element("propname"); xml.q .write_event_async(Event::Empty(empty_propname)) .await? } Self::AllProp(maybe_include) => { let empty_allprop = xml.create_dav_element("allprop"); xml.q.write_event_async(Event::Empty(empty_allprop)).await?; if let Some(include) = maybe_include { include.qwrite(xml).await?; } } Self::Prop(propname) => propname.qwrite(xml).await?, } xml.q.write_event_async(Event::End(end)).await } } /// PROPPATCH REQUEST impl QWrite for PropertyUpdate { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("propertyupdate"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for update in self.0.iter() { update.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await } } /// PROPFIND RESPONSE, PROPPATCH RESPONSE, COPY RESPONSE, MOVE RESPONSE /// DELETE RESPONSE, 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(); xml.q.write_event_async(Event::Start(start.clone())).await?; for response in self.responses.iter() { response.qwrite(xml).await?; } if let Some(description) = &self.responsedescription { description.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await?; Ok(()) } } /// LOCK REQUEST impl QWrite for LockInfo { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("lockinfo"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; self.lockscope.qwrite(xml).await?; self.locktype.qwrite(xml).await?; if let Some(owner) = &self.owner { owner.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await } } /// SOME LOCK RESPONSES impl QWrite for PropValue { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("prop"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for propval in &self.0 { propval.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await } } /// Error response impl QWrite for Error { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("error"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for violation in &self.0 { violation.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await } } // --- XML inner elements impl QWrite for PropertyUpdateItem { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { match self { Self::Set(set) => set.qwrite(xml).await, Self::Remove(rm) => rm.qwrite(xml).await, } } } impl QWrite for Set { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("set"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; self.0.qwrite(xml).await?; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for Remove { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("remove"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; self.0.qwrite(xml).await?; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for PropName { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("prop"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for propname in &self.0 { propname.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await } } impl QWrite for AnyProp { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("prop"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for propname in &self.0 { propname.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await } } impl QWrite for AnyProperty { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { match self { Self::Request(v) => v.qwrite(xml).await, Self::Value(v) => v.qwrite(xml).await, } } } impl QWrite for Href { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("href"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; xml.q .write_event_async(Event::Text(BytesText::new(&self.0))) .await?; xml.q.write_event_async(Event::End(end)).await } } 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(); xml.q.write_event_async(Event::Start(start.clone())).await?; self.status_or_propstat.qwrite(xml).await?; if let Some(error) = &self.error { error.qwrite(xml).await?; } if let Some(responsedescription) = &self.responsedescription { responsedescription.qwrite(xml).await?; } if let Some(location) = &self.location { location.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await } } impl QWrite for StatusOrPropstat { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { match self { Self::Status(many_href, status) => { for href in many_href.iter() { href.qwrite(xml).await?; } status.qwrite(xml).await } Self::PropStat(href, propstat_list) => { href.qwrite(xml).await?; for propstat in propstat_list.iter() { propstat.qwrite(xml).await?; } Ok(()) } } } } impl QWrite for Status { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("status"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; let txt = format!( "HTTP/1.1 {} {}", self.0.as_str(), self.0.canonical_reason().unwrap_or("No reason") ); xml.q .write_event_async(Event::Text(BytesText::new(&txt))) .await?; xml.q.write_event_async(Event::End(end)).await?; Ok(()) } } impl QWrite for ResponseDescription { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("responsedescription"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; xml.q .write_event_async(Event::Text(BytesText::new(&self.0))) .await?; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for Location { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("location"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; self.0.qwrite(xml).await?; xml.q.write_event_async(Event::End(end)).await } } 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(); xml.q.write_event_async(Event::Start(start.clone())).await?; self.prop.qwrite(xml).await?; self.status.qwrite(xml).await?; if let Some(error) = &self.error { error.qwrite(xml).await?; } if let Some(description) = &self.responsedescription { description.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await?; Ok(()) } } impl QWrite for Property { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { use Property::*; match self { CreationDate(date) => { // 1997-12-01T17:42:21-08:00 let start = xml.create_dav_element("creationdate"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; xml.q .write_event_async(Event::Text(BytesText::new(&date.to_rfc3339()))) .await?; xml.q.write_event_async(Event::End(end)).await?; } DisplayName(name) => { // Example collection let start = xml.create_dav_element("displayname"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; xml.q .write_event_async(Event::Text(BytesText::new(name))) .await?; xml.q.write_event_async(Event::End(end)).await?; } GetContentLanguage(lang) => { let start = xml.create_dav_element("getcontentlanguage"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; xml.q .write_event_async(Event::Text(BytesText::new(lang))) .await?; xml.q.write_event_async(Event::End(end)).await?; } GetContentLength(len) => { // 4525 let start = xml.create_dav_element("getcontentlength"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; xml.q .write_event_async(Event::Text(BytesText::new(&len.to_string()))) .await?; xml.q.write_event_async(Event::End(end)).await?; } GetContentType(ct) => { // text/html let start = xml.create_dav_element("getcontenttype"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; xml.q .write_event_async(Event::Text(BytesText::new(&ct))) .await?; xml.q.write_event_async(Event::End(end)).await?; } GetEtag(et) => { // "zzyzx" let start = xml.create_dav_element("getetag"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; xml.q .write_event_async(Event::Text(BytesText::new(et))) .await?; xml.q.write_event_async(Event::End(end)).await?; } GetLastModified(date) => { // Mon, 12 Jan 1998 09:25:56 GMT let start = xml.create_dav_element("getlastmodified"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; xml.q .write_event_async(Event::Text(BytesText::new(&date.to_rfc2822()))) .await?; xml.q.write_event_async(Event::End(end)).await?; } LockDiscovery(many_locks) => { // ... let start = xml.create_dav_element("lockdiscovery"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for lock in many_locks.iter() { lock.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await?; } ResourceType(many_types) => { // // // // // // let start = xml.create_dav_element("resourcetype"); if many_types.is_empty() { xml.q.write_event_async(Event::Empty(start)).await?; } else { let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for restype in many_types.iter() { restype.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await?; } } SupportedLock(many_entries) => { // // ... let start = xml.create_dav_element("supportedlock"); if many_entries.is_empty() { xml.q.write_event_async(Event::Empty(start)).await?; } else { let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for entry in many_entries.iter() { entry.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await?; } } Extension(inner) => inner.qwrite(xml).await?, }; Ok(()) } } impl QWrite for ResourceType { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { match self { Self::Collection => { let empty_collection = xml.create_dav_element("collection"); xml.q .write_event_async(Event::Empty(empty_collection)) .await } Self::Extension(inner) => inner.qwrite(xml).await, } } } impl QWrite for Include { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("include"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for prop in self.0.iter() { prop.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await } } impl QWrite for PropertyRequest { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { use PropertyRequest::*; let mut atom = async |c| { let empty_tag = xml.create_dav_element(c); xml.q.write_event_async(Event::Empty(empty_tag)).await }; match self { CreationDate => atom("creationdate").await, DisplayName => atom("displayname").await, GetContentLanguage => atom("getcontentlanguage").await, GetContentLength => atom("getcontentlength").await, GetContentType => atom("getcontenttype").await, GetEtag => atom("getetag").await, GetLastModified => atom("getlastmodified").await, LockDiscovery => atom("lockdiscovery").await, ResourceType => atom("resourcetype").await, SupportedLock => atom("supportedlock").await, Extension(inner) => inner.qwrite(xml).await, } } } impl QWrite for ActiveLock { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { // // // // infinity // // http://example.org/~ejw/contact.html // // Second-604800 // // urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4 // // // http://example.com/workspace/webdav/proposal.doc // // let start = xml.create_dav_element("activelock"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; self.locktype.qwrite(xml).await?; self.lockscope.qwrite(xml).await?; self.depth.qwrite(xml).await?; if let Some(owner) = &self.owner { owner.qwrite(xml).await?; } if let Some(timeout) = &self.timeout { timeout.qwrite(xml).await?; } if let Some(locktoken) = &self.locktoken { locktoken.qwrite(xml).await?; } self.lockroot.qwrite(xml).await?; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for LockType { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("locktype"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; match self { Self::Write => { let empty_write = xml.create_dav_element("write"); xml.q.write_event_async(Event::Empty(empty_write)).await? } }; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for LockScope { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("lockscope"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; match self { Self::Exclusive => { let empty_tag = xml.create_dav_element("exclusive"); xml.q.write_event_async(Event::Empty(empty_tag)).await? } Self::Shared => { let empty_tag = xml.create_dav_element("shared"); xml.q.write_event_async(Event::Empty(empty_tag)).await? } }; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for Owner { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("owner"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; match self { Self::Txt(txt) => { xml.q .write_event_async(Event::Text(BytesText::new(&txt))) .await? } Self::Href(href) => href.qwrite(xml).await?, Self::Unknown => (), } xml.q.write_event_async(Event::End(end)).await } } impl QWrite for Depth { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("depth"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; match self { Self::Zero => { xml.q .write_event_async(Event::Text(BytesText::new("0"))) .await? } Self::One => { xml.q .write_event_async(Event::Text(BytesText::new("1"))) .await? } Self::Infinity => { xml.q .write_event_async(Event::Text(BytesText::new("infinity"))) .await? } }; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for Timeout { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("timeout"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; match self { Self::Seconds(count) => { let txt = format!("Second-{}", count); xml.q .write_event_async(Event::Text(BytesText::new(&txt))) .await? } Self::Infinite => { xml.q .write_event_async(Event::Text(BytesText::new("Infinite"))) .await? } }; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for LockToken { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("locktoken"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; self.0.qwrite(xml).await?; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for LockRoot { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("lockroot"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; self.0.qwrite(xml).await?; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for LockEntry { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("lockentry"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; self.lockscope.qwrite(xml).await?; self.locktype.qwrite(xml).await?; xml.q.write_event_async(Event::End(end)).await } } impl QWrite for Violation { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let mut atom = async |c| { let empty_tag = xml.create_dav_element(c); xml.q.write_event_async(Event::Empty(empty_tag)).await }; match self { Violation::LockTokenMatchesRequestUri => atom("lock-token-matches-request-uri").await, Violation::LockTokenSubmitted(hrefs) if hrefs.is_empty() => { atom("lock-token-submitted").await } Violation::LockTokenSubmitted(hrefs) => { let start = xml.create_dav_element("lock-token-submitted"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for href in hrefs { href.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await } Violation::NoConflictingLock(hrefs) if hrefs.is_empty() => { atom("no-conflicting-lock").await } Violation::NoConflictingLock(hrefs) => { let start = xml.create_dav_element("no-conflicting-lock"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; for href in hrefs { href.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await } Violation::NoExternalEntities => atom("no-external-entities").await, Violation::PreservedLiveProperties => atom("preserved-live-properties").await, Violation::PropfindFiniteDepth => atom("propfind-finite-depth").await, Violation::CannotModifyProtectedProperty => { atom("cannot-modify-protected-property").await } Violation::Extension(inner) => inner.qwrite(xml).await, } } } #[cfg(test)] mod tests { use super::super::xml; use super::*; use crate::realization::Core; use tokio::io::AsyncWriteExt; /// To run only the unit tests and avoid the behavior ones: /// cargo test --bin aerogramme async fn serialize(elem: &impl QWrite) -> String { let mut buffer = Vec::new(); let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer); let q = quick_xml::writer::Writer::new_with_indent(&mut tokio_buffer, b' ', 4); let ns_to_apply = vec![("xmlns:D".into(), "DAV:".into())]; let mut writer = Writer { q, ns_to_apply }; elem.qwrite(&mut writer).await.expect("xml serialization"); tokio_buffer.flush().await.expect("tokio buffer flush"); let got = std::str::from_utf8(buffer.as_slice()).unwrap(); return got.into(); } async fn deserialize>(src: &str) -> T { let mut rdr = xml::Reader::new(quick_xml::reader::NsReader::from_reader(src.as_bytes())) .await .unwrap(); rdr.find().await.unwrap() } #[tokio::test] async fn basic_href() { let orig = Href("/SOGo/dav/so/".into()); let got = serialize(&orig).await; let expected = r#"/SOGo/dav/so/"#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::(got.as_str()).await, orig) } #[tokio::test] async fn basic_multistatus() { let orig = Multistatus:: { responses: vec![], responsedescription: Some(ResponseDescription("Hello world".into())), }; let got = serialize(&orig).await; let expected = r#" Hello world "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::>(got.as_str()).await, orig) } #[tokio::test] async fn rfc_error_delete_locked() { let orig = Error::(vec![Violation::LockTokenSubmitted(vec![Href( "/locked/".into(), )])]); let got = serialize(&orig).await; let expected = r#" /locked/ "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::>(got.as_str()).await, orig) } #[tokio::test] async fn rfc_propname_req() { let orig = PropFind::::PropName; let got = serialize(&orig).await; let expected = r#" "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::>(got.as_str()).await, orig) } #[tokio::test] async fn rfc_propname_res() { let orig = 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 got = serialize(&orig).await; let expected = r#" http://www.example.com/container/ HTTP/1.1 200 OK http://www.example.com/container/front.html HTTP/1.1 200 OK "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::>(got.as_str()).await, orig) } #[tokio::test] async fn rfc_allprop_req() { let orig = PropFind::::AllProp(None); let got = serialize(&orig).await; let expected = r#" "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::>(got.as_str()).await, orig) } #[tokio::test] async fn rfc_allprop_res() { use chrono::{FixedOffset, TimeZone}; let orig = 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, 1, 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, 1, 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::east_opt(0) .unwrap() .with_ymd_and_hms(1998, 1, 12, 9, 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 got = serialize(&orig).await; let expected = r#" /container/ 1997-12-01T17:42:21-08:00 Example collection HTTP/1.1 200 OK /container/front.html 1997-12-01T18:27:21-08:00 Example HTML resource 4525 text/html "zzyzx" Mon, 12 Jan 1998 09:25:56 +0000 HTTP/1.1 200 OK "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::>(got.as_str()).await, orig) } #[tokio::test] async fn rfc_allprop_include() { let orig = PropFind::::AllProp(Some(Include(vec![ PropertyRequest::DisplayName, PropertyRequest::ResourceType, ]))); let got = serialize(&orig).await; let expected = r#" "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::>(got.as_str()).await, orig) } #[tokio::test] async fn rfc_propertyupdate() { let orig = PropertyUpdate::(vec![ PropertyUpdateItem::Set(Set(PropValue(vec![Property::GetContentLanguage( "fr-FR".into(), )]))), PropertyUpdateItem::Remove(Remove(PropName(vec![PropertyRequest::DisplayName]))), ]); let got = serialize(&orig).await; let expected = r#" fr-FR "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!( deserialize::>(got.as_str()).await, orig ) } #[tokio::test] async fn rfc_delete_locked2() { let orig = Multistatus:: { responses: vec![Response { status_or_propstat: StatusOrPropstat::Status( vec![Href("http://www.example.com/container/resource3".into())], Status(http::status::StatusCode::from_u16(423).unwrap()), ), error: Some(Error(vec![Violation::LockTokenSubmitted(vec![])])), responsedescription: None, location: None, }], responsedescription: None, }; let got = serialize(&orig).await; let expected = r#" http://www.example.com/container/resource3 HTTP/1.1 423 Locked "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::>(got.as_str()).await, orig) } #[tokio::test] async fn rfc_simple_lock_request() { let orig = LockInfo { lockscope: LockScope::Exclusive, locktype: LockType::Write, owner: Some(Owner::Href(Href( "http://example.org/~ejw/contact.html".into(), ))), }; let got = serialize(&orig).await; let expected = r#" http://example.org/~ejw/contact.html "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::(got.as_str()).await, orig) } #[tokio::test] async fn rfc_simple_lock_response() { let orig = PropValue::(vec![Property::LockDiscovery(vec![ActiveLock { lockscope: LockScope::Exclusive, locktype: LockType::Write, depth: Depth::Infinity, owner: Some(Owner::Href(Href( "http://example.org/~ejw/contact.html".into(), ))), timeout: Some(Timeout::Seconds(604800)), locktoken: Some(LockToken(Href( "urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4".into(), ))), lockroot: LockRoot(Href( "http://example.com/workspace/webdav/proposal.doc".into(), )), }])]); let got = serialize(&orig).await; let expected = r#" infinity http://example.org/~ejw/contact.html Second-604800 urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4 http://example.com/workspace/webdav/proposal.doc "#; assert_eq!( &got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n" ); assert_eq!(deserialize::>(got.as_str()).await, orig) } }