use std::io::Cursor; use quick_xml::Error as QError; use quick_xml::events::{Event, BytesEnd, BytesStart, BytesText}; use quick_xml::writer::ElementWriter; use quick_xml::name::PrefixDeclaration; use tokio::io::AsyncWrite; use super::types::*; use super::xml::{Writer,QWrite,IWrite}; // --- 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 AnyProp { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { match self { Self::Name(propname) => propname.qwrite(xml).await, Self::Value(propval) => propval.qwrite(xml).await, } } } impl QWrite for PropName { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("prop"); 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 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.href.qwrite(xml).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(status) => status.qwrite(xml).await, Self::PropStat(propstat_list) => { 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?, } 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::*; use crate::dav::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() } #[tokio::test] async fn basic_href() { let got = serialize( &Href("/SOGo/dav/so/".into()) ).await; let expected = r#"/SOGo/dav/so/"#; assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); } #[tokio::test] async fn basic_multistatus() { let got = serialize( &Multistatus:: { responses: vec![], responsedescription: Some(ResponseDescription("Hello world".into())) }, ).await; let expected = r#" Hello world "#; assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); } #[tokio::test] async fn rfc_error_delete_locked() { let got = serialize( &Error::(vec![ Violation::LockTokenSubmitted(vec![ Href("/locked/".into()) ]) ]), ).await; let expected = r#" /locked/ "#; assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); } #[tokio::test] async fn rfc_propname_req() { let got = serialize( &PropFind::::PropName, ).await; let expected = r#" "#; assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); } #[tokio::test] async fn rfc_propname_res() { let got = serialize( &Multistatus:: { responses: vec![ Response { href: Href("http://www.example.com/container/".into()), status_or_propstat: StatusOrPropstat::PropStat(vec![PropStat { prop: AnyProp::Name(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 { href: Href("http://www.example.com/container/front.html".into()), status_or_propstat: StatusOrPropstat::PropStat(vec![PropStat { prop: AnyProp::Name(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, }, ).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"); } #[tokio::test] async fn rfc_allprop_req() { let got = serialize( &PropFind::::AllProp(None), ).await; let expected = r#" "#; assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); } #[tokio::test] async fn rfc_allprop_res() { use chrono::{DateTime,FixedOffset,TimeZone}; let got = serialize( &Multistatus:: { responses: vec![ Response { href: Href("/container/".into()), status_or_propstat: StatusOrPropstat::PropStat(vec![PropStat { prop: AnyProp::Value(PropValue(vec![ Property::CreationDate(FixedOffset::west_opt(8 * 3600) .unwrap() .with_ymd_and_hms(1997, 12, 1, 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 { href: Href("/container/front.html".into()), status_or_propstat: StatusOrPropstat::PropStat(vec![PropStat { prop: AnyProp::Value(PropValue(vec![ Property::CreationDate(FixedOffset::west_opt(8 * 3600) .unwrap() .with_ymd_and_hms(1997, 12, 1, 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::east_opt(0) .unwrap() .with_ymd_and_hms(1998, 1, 12, 9, 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, } ).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"); } #[tokio::test] async fn rfc_allprop_include() { let got = serialize( &PropFind::::AllProp(Some(Include(vec![ PropertyRequest::DisplayName, PropertyRequest::ResourceType, ]))), ).await; let expected = r#" "#; assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); } #[tokio::test] async fn rfc_propertyupdate() { let got = serialize( &PropertyUpdate::(vec![ PropertyUpdateItem::Set(Set(PropValue(vec![ Property::GetContentLanguage("fr-FR".into()), ]))), PropertyUpdateItem::Remove(Remove(PropName(vec![ PropertyRequest::DisplayName, ]))), ]), ).await; let expected = r#" fr-FR "#; assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); } #[tokio::test] async fn rfc_delete_locked2() { let got = serialize( &Multistatus:: { responses: vec![Response { href: Href("http://www.example.com/container/resource3".into()), status_or_propstat: StatusOrPropstat::Status(Status(http::status::StatusCode::from_u16(423).unwrap())), error: Some(Error(vec![Violation::LockTokenSubmitted(vec![])])), responsedescription: None, location: None, }], responsedescription: None, }, ).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"); } #[tokio::test] async fn rfc_simple_lock_request() { let got = serialize( &LockInfo { lockscope: LockScope::Exclusive, locktype: LockType::Write, owner: Some(Owner::Href(Href("http://example.org/~ejw/contact.html".into()))), }, ).await; let expected = r#" http://example.org/~ejw/contact.html "#; assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); } #[tokio::test] async fn rfc_simple_lock_response() { let got = serialize( &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())), }]), ]), ).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"); } }