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/fuzz/fuzz_targets/dav.rs | 157 +++++---- aero-dav/src/acldecoder.rs | 44 ++- aero-dav/src/aclencoder.rs | 12 +- aero-dav/src/acltypes.rs | 2 - aero-dav/src/caldecoder.rs | 696 +++++++++++++++++++++++++------------- aero-dav/src/calencoder.rs | 431 ++++++++++++++--------- aero-dav/src/caltypes.rs | 53 ++- aero-dav/src/decoder.rs | 674 ++++++++++++++++++++++-------------- aero-dav/src/encoder.rs | 435 +++++++++++++++--------- aero-dav/src/error.rs | 4 +- aero-dav/src/lib.rs | 12 +- aero-dav/src/realization.rs | 38 ++- aero-dav/src/types.rs | 29 +- aero-dav/src/xml.rs | 124 ++++--- 14 files changed, 1686 insertions(+), 1025 deletions(-) (limited to 'aero-dav') diff --git a/aero-dav/fuzz/fuzz_targets/dav.rs b/aero-dav/fuzz/fuzz_targets/dav.rs index 5bd28bc..a303401 100644 --- a/aero-dav/fuzz/fuzz_targets/dav.rs +++ b/aero-dav/fuzz/fuzz_targets/dav.rs @@ -1,79 +1,79 @@ #![no_main] -use libfuzzer_sys::fuzz_target; use libfuzzer_sys::arbitrary; use libfuzzer_sys::arbitrary::Arbitrary; +use libfuzzer_sys::fuzz_target; -use aero_dav::{types, realization, xml}; +use aero_dav::{realization, types, xml}; use quick_xml::reader::NsReader; -use tokio::runtime::Runtime; use tokio::io::AsyncWriteExt; +use tokio::runtime::Runtime; // Split this file const tokens: [&str; 63] = [ -"0", -"1", -"activelock", -"allprop", -"encoding", -"utf-8", -"http://ns.example.com/boxschema/", -"HTTP/1.1 200 OK", -"1997-12-01T18:27:21-08:00", -"Mon, 12 Jan 1998 09:25:56 GMT", -"\"abcdef\"", -"cannot-modify-protected-property", -"collection", -"creationdate", -"DAV:", -"D", -"C", -"xmlns:D", -"depth", -"displayname", -"error", -"exclusive", -"getcontentlanguage", -"getcontentlength", -"getcontenttype", -"getetag", -"getlastmodified", -"href", -"include", -"Infinite", -"infinity", -"location", -"lockdiscovery", -"lockentry", -"lockinfo", -"lockroot", -"lockscope", -"locktoken", -"lock-token-matches-request-uri", -"lock-token-submitted", -"locktype", -"multistatus", -"no-conflicting-lock", -"no-external-entities", -"owner", -"preserved-live-properties", -"prop", -"propertyupdate", -"propfind", -"propfind-finite-depth", -"propname", -"propstat", -"remove", -"resourcetype", -"response", -"responsedescription", -"set", -"shared", -"status", -"supportedlock", -"text/html", -"timeout", -"write", + "0", + "1", + "activelock", + "allprop", + "encoding", + "utf-8", + "http://ns.example.com/boxschema/", + "HTTP/1.1 200 OK", + "1997-12-01T18:27:21-08:00", + "Mon, 12 Jan 1998 09:25:56 GMT", + "\"abcdef\"", + "cannot-modify-protected-property", + "collection", + "creationdate", + "DAV:", + "D", + "C", + "xmlns:D", + "depth", + "displayname", + "error", + "exclusive", + "getcontentlanguage", + "getcontentlength", + "getcontenttype", + "getetag", + "getlastmodified", + "href", + "include", + "Infinite", + "infinity", + "location", + "lockdiscovery", + "lockentry", + "lockinfo", + "lockroot", + "lockscope", + "locktoken", + "lock-token-matches-request-uri", + "lock-token-submitted", + "locktype", + "multistatus", + "no-conflicting-lock", + "no-external-entities", + "owner", + "preserved-live-properties", + "prop", + "propertyupdate", + "propfind", + "propfind-finite-depth", + "propname", + "propstat", + "remove", + "resourcetype", + "response", + "responsedescription", + "set", + "shared", + "status", + "supportedlock", + "text/html", + "timeout", + "write", ]; #[derive(Arbitrary)] @@ -106,7 +106,7 @@ impl Tag { acc.push_str("D:"); acc.push_str(self.name.serialize().as_str()); - if let Some((k,v)) = &self.attr { + if let Some((k, v)) = &self.attr { acc.push_str(" "); acc.push_str(k.serialize().as_str()); acc.push_str("=\""); @@ -123,7 +123,6 @@ impl Tag { } } - #[derive(Arbitrary)] enum XmlNode { //@FIXME: build RFC3339 and RFC822 Dates with chrono based on timestamps @@ -145,9 +144,14 @@ impl XmlNode { let stag = tag.start(); match children.is_empty() { true => format!("<{}/>", stag), - false => format!("<{}>{}", stag, children.iter().map(|v| v.serialize()).collect::(), tag.end()), + false => format!( + "<{}>{}", + stag, + children.iter().map(|v| v.serialize()).collect::(), + tag.end() + ), } - }, + } Self::Number(v) => format!("{}", v), Self::Text(v) => v.serialize(), } @@ -158,19 +162,22 @@ async fn serialize(elem: &impl xml::QWrite) -> Vec { 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 ns_to_apply = vec![("xmlns:D".into(), "DAV:".into())]; let mut writer = xml::Writer { q, ns_to_apply }; elem.qwrite(&mut writer).await.expect("xml serialization"); tokio_buffer.flush().await.expect("tokio buffer flush"); - return buffer + return buffer; } type Object = types::Multistatus>; fuzz_target!(|nodes: XmlNode| { - let gen = format!("{}", nodes.serialize()); + let gen = format!( + "{}", + nodes.serialize() + ); //println!("--------\n{}", gen); let data = gen.as_bytes(); @@ -191,7 +198,9 @@ fuzz_target!(|nodes: XmlNode| { let my_serialization = serialize(&reference).await; // 3. De-serialize my serialization - let mut rdr2 = xml::Reader::new(NsReader::from_reader(my_serialization.as_slice())).await.expect("XML Reader init"); + let mut rdr2 = xml::Reader::new(NsReader::from_reader(my_serialization.as_slice())) + .await + .expect("XML Reader init"); let comparison = rdr2.find::().await.expect("Deserialize again"); // 4. Both the first decoding and last decoding must be identical diff --git a/aero-dav/src/acldecoder.rs b/aero-dav/src/acldecoder.rs index 67dfb0b..405286e 100644 --- a/aero-dav/src/acldecoder.rs +++ b/aero-dav/src/acldecoder.rs @@ -1,23 +1,31 @@ use super::acltypes::*; -use super::types as dav; -use super::xml::{QRead, Reader, IRead, DAV_URN}; use super::error::ParsingError; +use super::types as dav; +use super::xml::{IRead, QRead, Reader, DAV_URN}; impl QRead for Property { async fn qread(xml: &mut Reader) -> Result { if xml.maybe_open_start(DAV_URN, "owner").await?.is_some() { let href = xml.find().await?; xml.close().await?; - return Ok(Self::Owner(href)) + return Ok(Self::Owner(href)); } - if xml.maybe_open_start(DAV_URN, "current-user-principal").await?.is_some() { + if xml + .maybe_open_start(DAV_URN, "current-user-principal") + .await? + .is_some() + { let user = xml.find().await?; xml.close().await?; - return Ok(Self::CurrentUserPrincipal(user)) + return Ok(Self::CurrentUserPrincipal(user)); } - if xml.maybe_open_start(DAV_URN, "current-user-privilege-set").await?.is_some() { + if xml + .maybe_open_start(DAV_URN, "current-user-privilege-set") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::CurrentUserPrivilegeSet(vec![])) + return Ok(Self::CurrentUserPrivilegeSet(vec![])); } Err(ParsingError::Recoverable) @@ -28,17 +36,25 @@ impl QRead for PropertyRequest { async fn qread(xml: &mut Reader) -> Result { if xml.maybe_open(DAV_URN, "owner").await?.is_some() { xml.close().await?; - return Ok(Self::Owner) + return Ok(Self::Owner); } - if xml.maybe_open(DAV_URN, "current-user-principal").await?.is_some() { + if xml + .maybe_open(DAV_URN, "current-user-principal") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::CurrentUserPrincipal) + return Ok(Self::CurrentUserPrincipal); } - if xml.maybe_open(DAV_URN, "current-user-privilege-set").await?.is_some() { + if xml + .maybe_open(DAV_URN, "current-user-privilege-set") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::CurrentUserPrivilegeSet) + return Ok(Self::CurrentUserPrivilegeSet); } Err(ParsingError::Recoverable) @@ -49,7 +65,7 @@ impl QRead for ResourceType { async fn qread(xml: &mut Reader) -> Result { if xml.maybe_open(DAV_URN, "principal").await?.is_some() { xml.close().await?; - return Ok(Self::Principal) + return Ok(Self::Principal); } Err(ParsingError::Recoverable) } @@ -60,7 +76,7 @@ impl QRead for User { async fn qread(xml: &mut Reader) -> Result { if xml.maybe_open(DAV_URN, "unauthenticated").await?.is_some() { xml.close().await?; - return Ok(Self::Unauthenticated) + return Ok(Self::Unauthenticated); } dav::Href::qread(xml).await.map(Self::Authenticated) diff --git a/aero-dav/src/aclencoder.rs b/aero-dav/src/aclencoder.rs index 2fa4707..28c01a7 100644 --- a/aero-dav/src/aclencoder.rs +++ b/aero-dav/src/aclencoder.rs @@ -1,9 +1,9 @@ -use quick_xml::Error as QError; use quick_xml::events::Event; +use quick_xml::Error as QError; use super::acltypes::*; -use super::xml::{QWrite, Writer, IWrite}; use super::error::ParsingError; +use super::xml::{IWrite, QWrite, Writer}; impl QWrite for Property { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { @@ -14,18 +14,18 @@ impl QWrite for Property { xml.q.write_event_async(Event::Start(start.clone())).await?; href.qwrite(xml).await?; xml.q.write_event_async(Event::End(end)).await - }, + } Self::CurrentUserPrincipal(user) => { let start = xml.create_dav_element("current-user-principal"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; user.qwrite(xml).await?; xml.q.write_event_async(Event::End(end)).await - }, + } Self::CurrentUserPrivilegeSet(_) => { let empty_tag = xml.create_dav_element("current-user-privilege-set"); xml.q.write_event_async(Event::Empty(empty_tag)).await - }, + } } } } @@ -64,7 +64,7 @@ impl QWrite for User { Self::Unauthenticated => { let tag = xml.create_dav_element("unauthenticated"); xml.q.write_event_async(Event::Empty(tag)).await - }, + } Self::Authenticated(href) => href.qwrite(xml).await, } } diff --git a/aero-dav/src/acltypes.rs b/aero-dav/src/acltypes.rs index d5be413..0af3c8a 100644 --- a/aero-dav/src/acltypes.rs +++ b/aero-dav/src/acltypes.rs @@ -2,14 +2,12 @@ use super::types as dav; //RFC covered: RFC3744 (ACL core) + RFC5397 (ACL Current Principal Extension) - //@FIXME required for a full CalDAV implementation // See section 6. of the CalDAV RFC // It seems mainly required for free-busy that I will not implement now. // It can also be used for discovering main calendar, not sure it is used. // Note: it is used by Thunderbird - #[derive(Debug, PartialEq, Clone)] pub enum PropertyRequest { Owner, diff --git a/aero-dav/src/caldecoder.rs b/aero-dav/src/caldecoder.rs index 008668e..16c9c6c 100644 --- a/aero-dav/src/caldecoder.rs +++ b/aero-dav/src/caldecoder.rs @@ -1,10 +1,10 @@ -use quick_xml::events::Event; use chrono::NaiveDateTime; +use quick_xml::events::Event; -use super::types as dav; use super::caltypes::*; -use super::xml::{QRead, IRead, Reader, DAV_URN, CAL_URN}; use super::error::ParsingError; +use super::types as dav; +use super::xml::{IRead, QRead, Reader, CAL_URN, DAV_URN}; // ---- ROOT ELEMENTS --- impl QRead> for MkCalendar { @@ -29,7 +29,7 @@ impl QRead> for Report { async fn qread(xml: &mut Reader) -> Result { match CalendarQuery::::qread(xml).await { Err(ParsingError::Recoverable) => (), - otherwise => return otherwise.map(Self::Query) + otherwise => return otherwise.map(Self::Query), } match CalendarMultiget::::qread(xml).await { @@ -61,7 +61,11 @@ impl QRead> for CalendarQuery { xml.close().await?; match filter { - Some(filter) => Ok(CalendarQuery { selector, filter, timezone }), + Some(filter) => Ok(CalendarQuery { + selector, + filter, + timezone, + }), _ => Err(ParsingError::MissingChild), } } @@ -100,39 +104,70 @@ impl QRead for FreeBusyQuery { } } - // ---- EXTENSIONS --- impl QRead for Violation { async fn qread(xml: &mut Reader) -> Result { - if xml.maybe_open(DAV_URN, "resource-must-be-null").await?.is_some() { + if xml + .maybe_open(DAV_URN, "resource-must-be-null") + .await? + .is_some() + { xml.close().await?; Ok(Self::ResourceMustBeNull) } else if xml.maybe_open(DAV_URN, "need-privileges").await?.is_some() { xml.close().await?; Ok(Self::NeedPrivileges) - } else if xml.maybe_open(CAL_URN, "calendar-collection-location-ok").await?.is_some() { + } else if xml + .maybe_open(CAL_URN, "calendar-collection-location-ok") + .await? + .is_some() + { xml.close().await?; Ok(Self::CalendarCollectionLocationOk) - } else if xml.maybe_open(CAL_URN, "valid-calendar-data").await?.is_some() { + } else if xml + .maybe_open(CAL_URN, "valid-calendar-data") + .await? + .is_some() + { xml.close().await?; Ok(Self::ValidCalendarData) - } else if xml.maybe_open(CAL_URN, "initialize-calendar-collection").await?.is_some() { + } else if xml + .maybe_open(CAL_URN, "initialize-calendar-collection") + .await? + .is_some() + { xml.close().await?; Ok(Self::InitializeCalendarCollection) - } else if xml.maybe_open(CAL_URN, "supported-calendar-data").await?.is_some() { + } else if xml + .maybe_open(CAL_URN, "supported-calendar-data") + .await? + .is_some() + { xml.close().await?; Ok(Self::SupportedCalendarData) - } else if xml.maybe_open(CAL_URN, "valid-calendar-object-resource").await?.is_some() { + } else if xml + .maybe_open(CAL_URN, "valid-calendar-object-resource") + .await? + .is_some() + { xml.close().await?; Ok(Self::ValidCalendarObjectResource) - } else if xml.maybe_open(CAL_URN, "supported-calendar-component").await?.is_some() { + } else if xml + .maybe_open(CAL_URN, "supported-calendar-component") + .await? + .is_some() + { xml.close().await?; Ok(Self::SupportedCalendarComponent) } else if xml.maybe_open(CAL_URN, "no-uid-conflict").await?.is_some() { let href = xml.find().await?; xml.close().await?; Ok(Self::NoUidConflict(href)) - } else if xml.maybe_open(CAL_URN, "max-resource-size").await?.is_some() { + } else if xml + .maybe_open(CAL_URN, "max-resource-size") + .await? + .is_some() + { xml.close().await?; Ok(Self::MaxResourceSize) } else if xml.maybe_open(CAL_URN, "min-date-time").await?.is_some() { @@ -144,7 +179,11 @@ impl QRead for Violation { } else if xml.maybe_open(CAL_URN, "max-instances").await?.is_some() { xml.close().await?; Ok(Self::MaxInstances) - } else if xml.maybe_open(CAL_URN, "max-attendees-per-instance").await?.is_some() { + } else if xml + .maybe_open(CAL_URN, "max-attendees-per-instance") + .await? + .is_some() + { xml.close().await?; Ok(Self::MaxAttendeesPerInstance) } else if xml.maybe_open(CAL_URN, "valid-filter").await?.is_some() { @@ -167,7 +206,11 @@ impl QRead for Violation { } xml.close().await?; Ok(Self::SupportedFilter { comp, prop, param }) - } else if xml.maybe_open(CAL_URN, "number-of-matches-within-limits").await?.is_some() { + } else if xml + .maybe_open(CAL_URN, "number-of-matches-within-limits") + .await? + .is_some() + { xml.close().await?; Ok(Self::NumberOfMatchesWithinLimits) } else { @@ -178,72 +221,112 @@ impl QRead for Violation { impl QRead for Property { async fn qread(xml: &mut Reader) -> Result { - if xml.maybe_open_start(CAL_URN, "calendar-home-set").await?.is_some() { + if xml + .maybe_open_start(CAL_URN, "calendar-home-set") + .await? + .is_some() + { let href = xml.find().await?; xml.close().await?; - return Ok(Property::CalendarHomeSet(href)) + return Ok(Property::CalendarHomeSet(href)); } - if xml.maybe_open_start(CAL_URN, "calendar-description").await?.is_some() { + if xml + .maybe_open_start(CAL_URN, "calendar-description") + .await? + .is_some() + { let lang = xml.prev_attr("xml:lang"); let text = xml.tag_string().await?; xml.close().await?; - return Ok(Property::CalendarDescription { lang, text }) + return Ok(Property::CalendarDescription { lang, text }); } - if xml.maybe_open_start(CAL_URN, "calendar-timezone").await?.is_some() { + if xml + .maybe_open_start(CAL_URN, "calendar-timezone") + .await? + .is_some() + { let tz = xml.tag_string().await?; xml.close().await?; - return Ok(Property::CalendarTimezone(tz)) + return Ok(Property::CalendarTimezone(tz)); } - if xml.maybe_open_start(CAL_URN, "supported-calendar-component-set").await?.is_some() { + if xml + .maybe_open_start(CAL_URN, "supported-calendar-component-set") + .await? + .is_some() + { let comp = xml.collect().await?; xml.close().await?; - return Ok(Property::SupportedCalendarComponentSet(comp)) + return Ok(Property::SupportedCalendarComponentSet(comp)); } - if xml.maybe_open_start(CAL_URN, "supported-calendar-data").await?.is_some() { + if xml + .maybe_open_start(CAL_URN, "supported-calendar-data") + .await? + .is_some() + { let mime = xml.collect().await?; xml.close().await?; - return Ok(Property::SupportedCalendarData(mime)) + return Ok(Property::SupportedCalendarData(mime)); } - if xml.maybe_open_start(CAL_URN, "max-resource-size").await?.is_some() { + if xml + .maybe_open_start(CAL_URN, "max-resource-size") + .await? + .is_some() + { let sz = xml.tag_string().await?.parse::()?; xml.close().await?; - return Ok(Property::MaxResourceSize(sz)) + return Ok(Property::MaxResourceSize(sz)); } - if xml.maybe_open_start(CAL_URN, "max-date-time").await?.is_some() { + if xml + .maybe_open_start(CAL_URN, "max-date-time") + .await? + .is_some() + { let dtstr = xml.tag_string().await?; let dt = NaiveDateTime::parse_from_str(dtstr.as_str(), ICAL_DATETIME_FMT)?.and_utc(); xml.close().await?; - return Ok(Property::MaxDateTime(dt)) + return Ok(Property::MaxDateTime(dt)); } - if xml.maybe_open_start(CAL_URN, "max-instances").await?.is_some() { + if xml + .maybe_open_start(CAL_URN, "max-instances") + .await? + .is_some() + { let sz = xml.tag_string().await?.parse::()?; xml.close().await?; - return Ok(Property::MaxInstances(sz)) + return Ok(Property::MaxInstances(sz)); } - if xml.maybe_open_start(CAL_URN, "max-attendees-per-instance").await?.is_some() { + if xml + .maybe_open_start(CAL_URN, "max-attendees-per-instance") + .await? + .is_some() + { let sz = xml.tag_string().await?.parse::()?; xml.close().await?; - return Ok(Property::MaxAttendeesPerInstance(sz)) + return Ok(Property::MaxAttendeesPerInstance(sz)); } - if xml.maybe_open_start(CAL_URN, "supported-collation-set").await?.is_some() { + if xml + .maybe_open_start(CAL_URN, "supported-collation-set") + .await? + .is_some() + { let cols = xml.collect().await?; xml.close().await?; - return Ok(Property::SupportedCollationSet(cols)) + return Ok(Property::SupportedCollationSet(cols)); } let mut dirty = false; let mut caldata: Option = None; xml.maybe_read(&mut caldata, &mut dirty).await?; if let Some(cal) = caldata { - return Ok(Property::CalendarData(cal)) + return Ok(Property::CalendarData(cal)); } Err(ParsingError::Recoverable) @@ -252,54 +335,88 @@ impl QRead for Property { impl QRead for PropertyRequest { async fn qread(xml: &mut Reader) -> Result { - if xml.maybe_open(CAL_URN, "calendar-home-set").await?.is_some() { + if xml + .maybe_open(CAL_URN, "calendar-home-set") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::CalendarHomeSet) - } - if xml.maybe_open(CAL_URN, "calendar-description").await?.is_some() { + return Ok(Self::CalendarHomeSet); + } + if xml + .maybe_open(CAL_URN, "calendar-description") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::CalendarDescription) - } - if xml.maybe_open(CAL_URN, "calendar-timezone").await?.is_some() { + return Ok(Self::CalendarDescription); + } + if xml + .maybe_open(CAL_URN, "calendar-timezone") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::CalendarTimezone) + return Ok(Self::CalendarTimezone); } - if xml.maybe_open(CAL_URN, "supported-calendar-component-set").await?.is_some() { + if xml + .maybe_open(CAL_URN, "supported-calendar-component-set") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::SupportedCalendarComponentSet) + return Ok(Self::SupportedCalendarComponentSet); } - if xml.maybe_open(CAL_URN, "supported-calendar-data").await?.is_some() { + if xml + .maybe_open(CAL_URN, "supported-calendar-data") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::SupportedCalendarData) + return Ok(Self::SupportedCalendarData); } - if xml.maybe_open(CAL_URN, "max-resource-size").await?.is_some() { + if xml + .maybe_open(CAL_URN, "max-resource-size") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::MaxResourceSize) + return Ok(Self::MaxResourceSize); } if xml.maybe_open(CAL_URN, "min-date-time").await?.is_some() { xml.close().await?; - return Ok(Self::MinDateTime) + return Ok(Self::MinDateTime); } if xml.maybe_open(CAL_URN, "max-date-time").await?.is_some() { xml.close().await?; - return Ok(Self::MaxDateTime) + return Ok(Self::MaxDateTime); } if xml.maybe_open(CAL_URN, "max-instances").await?.is_some() { xml.close().await?; - return Ok(Self::MaxInstances) + return Ok(Self::MaxInstances); } - if xml.maybe_open(CAL_URN, "max-attendees-per-instance").await?.is_some() { + if xml + .maybe_open(CAL_URN, "max-attendees-per-instance") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::MaxAttendeesPerInstance) + return Ok(Self::MaxAttendeesPerInstance); } - if xml.maybe_open(CAL_URN, "supported-collation-set").await?.is_some() { + if xml + .maybe_open(CAL_URN, "supported-collation-set") + .await? + .is_some() + { xml.close().await?; - return Ok(Self::SupportedCollationSet) + return Ok(Self::SupportedCollationSet); } let mut dirty = false; let mut m_cdr = None; xml.maybe_read(&mut m_cdr, &mut dirty).await?; - m_cdr.ok_or(ParsingError::Recoverable).map(Self::CalendarData) + m_cdr + .ok_or(ParsingError::Recoverable) + .map(Self::CalendarData) } } @@ -307,7 +424,7 @@ impl QRead for ResourceType { async fn qread(xml: &mut Reader) -> Result { if xml.maybe_open(CAL_URN, "calendar").await?.is_some() { xml.close().await?; - return Ok(Self::Calendar) + return Ok(Self::Calendar); } Err(ParsingError::Recoverable) } @@ -338,7 +455,10 @@ impl QRead for CalendarDataSupport { let ct = xml.prev_attr("content-type"); let vs = xml.prev_attr("version"); match (ct, vs) { - (Some(content_type), Some(version)) => Ok(Self { content_type, version }), + (Some(content_type), Some(version)) => Ok(Self { + content_type, + version, + }), _ => Err(ParsingError::Recoverable), } } @@ -351,10 +471,14 @@ impl QRead for CalendarDataRequest { let (mut comp, mut recurrence, mut limit_freebusy_set) = (None, None, None); if !xml.parent_has_child() { - return Ok(Self { mime, comp, recurrence, limit_freebusy_set }) + return Ok(Self { + mime, + comp, + recurrence, + limit_freebusy_set, + }); } - loop { let mut dirty = false; xml.maybe_read(&mut comp, &mut dirty).await?; @@ -367,11 +491,15 @@ impl QRead for CalendarDataRequest { _ => xml.skip().await?, }; } - } xml.close().await?; - Ok(Self { mime, comp, recurrence, limit_freebusy_set }) + Ok(Self { + mime, + comp, + recurrence, + limit_freebusy_set, + }) } } @@ -389,17 +517,25 @@ impl QRead for Comp { let (mut prop_kind, mut comp_kind) = (None, None); let bs = xml.open(CAL_URN, "comp").await?; - let name = Component::new(xml.prev_attr("name").ok_or(ParsingError::MissingAttribute)?); + let name = Component::new( + xml.prev_attr("name") + .ok_or(ParsingError::MissingAttribute)?, + ); // Return early if it's an empty tag if matches!(bs, Event::Empty(_)) { xml.close().await?; - return Ok(Self { name, prop_kind, comp_kind }) + return Ok(Self { + name, + prop_kind, + comp_kind, + }); } loop { - let mut dirty = false; - let (mut tmp_prop_kind, mut tmp_comp_kind): (Option, Option) = (None, None); + let mut dirty = false; + let (mut tmp_prop_kind, mut tmp_comp_kind): (Option, Option) = + (None, None); xml.maybe_read(&mut tmp_prop_kind, &mut dirty).await?; Box::pin(xml.maybe_read(&mut tmp_comp_kind, &mut dirty)).await?; @@ -408,35 +544,41 @@ impl QRead for Comp { // Merge match (tmp_prop_kind, &mut prop_kind) { (Some(PropKind::Prop(mut a)), Some(PropKind::Prop(ref mut b))) => b.append(&mut a), - (Some(PropKind::AllProp), v) => *v = Some(PropKind::AllProp), + (Some(PropKind::AllProp), v) => *v = Some(PropKind::AllProp), (Some(x), b) => *b = Some(x), (None, _) => (), }; match (tmp_comp_kind, &mut comp_kind) { (Some(CompKind::Comp(mut a)), Some(CompKind::Comp(ref mut b))) => b.append(&mut a), - (Some(CompKind::AllComp), v) => *v = Some(CompKind::AllComp), + (Some(CompKind::AllComp), v) => *v = Some(CompKind::AllComp), (Some(a), b) => *b = Some(a), (None, _) => (), }; - if !dirty { match xml.peek() { Event::End(_) => break, _ => xml.skip().await?, }; } - }; + } xml.close().await?; - Ok(Self { name, prop_kind, comp_kind }) + Ok(Self { + name, + prop_kind, + comp_kind, + }) } } impl QRead for CompSupport { async fn qread(xml: &mut Reader) -> Result { xml.open(CAL_URN, "comp").await?; - let inner = Component::new(xml.prev_attr("name").ok_or(ParsingError::MissingAttribute)?); + let inner = Component::new( + xml.prev_attr("name") + .ok_or(ParsingError::MissingAttribute)?, + ); xml.close().await?; Ok(Self(inner)) } @@ -450,18 +592,18 @@ impl QRead for CompKind { if xml.maybe_open(CAL_URN, "allcomp").await?.is_some() { xml.close().await?; - return Ok(CompKind::AllComp) + return Ok(CompKind::AllComp); } xml.maybe_push(&mut comp, &mut dirty).await?; if !dirty { - break + break; } } match &comp[..] { - [] => Err(ParsingError::Recoverable), - _ => Ok(CompKind::Comp(comp)), + [] => Err(ParsingError::Recoverable), + _ => Ok(CompKind::Comp(comp)), } } } @@ -474,13 +616,13 @@ impl QRead for PropKind { if xml.maybe_open(CAL_URN, "allprop").await?.is_some() { xml.close().await?; - return Ok(PropKind::AllProp) + return Ok(PropKind::AllProp); } xml.maybe_push(&mut prop, &mut dirty).await?; if !dirty { - break + break; } } @@ -497,7 +639,9 @@ impl QRead for RecurrenceModifier { Err(ParsingError::Recoverable) => (), otherwise => return otherwise.map(RecurrenceModifier::Expand), } - LimitRecurrenceSet::qread(xml).await.map(RecurrenceModifier::LimitRecurrenceSet) + LimitRecurrenceSet::qread(xml) + .await + .map(RecurrenceModifier::LimitRecurrenceSet) } } @@ -508,11 +652,11 @@ impl QRead for Expand { (Some(start), Some(end)) => (start, end), _ => return Err(ParsingError::MissingAttribute), }; - + let start = NaiveDateTime::parse_from_str(rstart.as_str(), ICAL_DATETIME_FMT)?.and_utc(); let end = NaiveDateTime::parse_from_str(rend.as_str(), ICAL_DATETIME_FMT)?.and_utc(); if start > end { - return Err(ParsingError::InvalidValue) + return Err(ParsingError::InvalidValue); } xml.close().await?; @@ -527,11 +671,11 @@ impl QRead for LimitRecurrenceSet { (Some(start), Some(end)) => (start, end), _ => return Err(ParsingError::MissingAttribute), }; - + let start = NaiveDateTime::parse_from_str(rstart.as_str(), ICAL_DATETIME_FMT)?.and_utc(); let end = NaiveDateTime::parse_from_str(rend.as_str(), ICAL_DATETIME_FMT)?.and_utc(); if start > end { - return Err(ParsingError::InvalidValue) + return Err(ParsingError::InvalidValue); } xml.close().await?; @@ -546,11 +690,11 @@ impl QRead for LimitFreebusySet { (Some(start), Some(end)) => (start, end), _ => return Err(ParsingError::MissingAttribute), }; - + let start = NaiveDateTime::parse_from_str(rstart.as_str(), ICAL_DATETIME_FMT)?.and_utc(); let end = NaiveDateTime::parse_from_str(rend.as_str(), ICAL_DATETIME_FMT)?.and_utc(); if start > end { - return Err(ParsingError::InvalidValue) + return Err(ParsingError::InvalidValue); } xml.close().await?; @@ -563,20 +707,21 @@ impl QRead> for CalendarSelector { // allprop if let Some(_) = xml.maybe_open(DAV_URN, "allprop").await? { xml.close().await?; - return Ok(Self::AllProp) + return Ok(Self::AllProp); } // propname if let Some(_) = xml.maybe_open(DAV_URN, "propname").await? { xml.close().await?; - return Ok(Self::PropName) + return Ok(Self::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 { - return Ok(Self::Prop(prop)) + return Ok(Self::Prop(prop)); } Err(ParsingError::Recoverable) @@ -586,10 +731,16 @@ impl QRead> for CalendarSelector { impl QRead for CompFilter { async fn qread(xml: &mut Reader) -> Result { xml.open(CAL_URN, "comp-filter").await?; - let name = Component::new(xml.prev_attr("name").ok_or(ParsingError::MissingAttribute)?); + let name = Component::new( + xml.prev_attr("name") + .ok_or(ParsingError::MissingAttribute)?, + ); let additional_rules = Box::pin(xml.maybe_find()).await?; xml.close().await?; - Ok(Self { name, additional_rules }) + Ok(Self { + name, + additional_rules, + }) } } @@ -604,7 +755,7 @@ impl QRead for CompFilterRules { if xml.maybe_open(CAL_URN, "is-not-defined").await?.is_some() { xml.close().await?; - return Ok(Self::IsNotDefined) + return Ok(Self::IsNotDefined); } xml.maybe_read(&mut time_range, &mut dirty).await?; @@ -621,7 +772,11 @@ impl QRead for CompFilterRules { match (&time_range, &prop_filter[..], &comp_filter[..]) { (None, [], []) => Err(ParsingError::Recoverable), - _ => Ok(Self::Matches(CompFilterMatch { time_range, prop_filter, comp_filter })), + _ => Ok(Self::Matches(CompFilterMatch { + time_range, + prop_filter, + comp_filter, + })), } } } @@ -635,10 +790,16 @@ impl QRead for CompFilterMatch { impl QRead for PropFilter { async fn qread(xml: &mut Reader) -> Result { xml.open(CAL_URN, "prop-filter").await?; - let name = ComponentProperty(xml.prev_attr("name").ok_or(ParsingError::MissingAttribute)?); + let name = ComponentProperty( + xml.prev_attr("name") + .ok_or(ParsingError::MissingAttribute)?, + ); let additional_rules = xml.maybe_find().await?; xml.close().await?; - Ok(Self { name, additional_rules }) + Ok(Self { + name, + additional_rules, + }) } } @@ -653,7 +814,7 @@ impl QRead for PropFilterRules { if xml.maybe_open(CAL_URN, "is-not-defined").await?.is_some() { xml.close().await?; - return Ok(Self::IsNotDefined) + return Ok(Self::IsNotDefined); } xml.maybe_read(&mut time_range, &mut dirty).await?; @@ -670,7 +831,11 @@ impl QRead for PropFilterRules { match (&time_range, &time_or_text, ¶m_filter[..]) { (None, None, []) => Err(ParsingError::Recoverable), - _ => Ok(PropFilterRules::Match(PropFilterMatch { time_range, time_or_text, param_filter })), + _ => Ok(PropFilterRules::Match(PropFilterMatch { + time_range, + time_or_text, + param_filter, + })), } } } @@ -684,10 +849,16 @@ impl QRead for PropFilterMatch { impl QRead for ParamFilter { async fn qread(xml: &mut Reader) -> Result { xml.open(CAL_URN, "param-filter").await?; - let name = PropertyParameter(xml.prev_attr("name").ok_or(ParsingError::MissingAttribute)?); + let name = PropertyParameter( + xml.prev_attr("name") + .ok_or(ParsingError::MissingAttribute)?, + ); let additional_rules = xml.maybe_find().await?; xml.close().await?; - Ok(Self { name, additional_rules }) + Ok(Self { + name, + additional_rules, + }) } } @@ -708,7 +879,11 @@ impl QRead for TextMatch { let negate_condition = xml.prev_attr("negate-condition").map(|v| v == "yes"); let text = xml.tag_string().await?; xml.close().await?; - Ok(Self { collation, negate_condition, text }) + Ok(Self { + collation, + negate_condition, + text, + }) } } @@ -716,7 +891,7 @@ impl QRead for ParamFilterMatch { async fn qread(xml: &mut Reader) -> Result { if xml.maybe_open(CAL_URN, "is-not-defined").await?.is_some() { xml.close().await?; - return Ok(Self::IsNotDefined) + return Ok(Self::IsNotDefined); } TextMatch::qread(xml).await.map(Self::Match) } @@ -745,11 +920,15 @@ impl QRead for TimeRange { xml.open(CAL_URN, "time-range").await?; let start = match xml.prev_attr("start") { - Some(r) => Some(NaiveDateTime::parse_from_str(r.as_str(), ICAL_DATETIME_FMT)?.and_utc()), + Some(r) => { + Some(NaiveDateTime::parse_from_str(r.as_str(), ICAL_DATETIME_FMT)?.and_utc()) + } _ => None, }; let end = match xml.prev_attr("end") { - Some(r) => Some(NaiveDateTime::parse_from_str(r.as_str(), ICAL_DATETIME_FMT)?.and_utc()), + Some(r) => { + Some(NaiveDateTime::parse_from_str(r.as_str(), ICAL_DATETIME_FMT)?.and_utc()) + } _ => None, }; @@ -758,10 +937,10 @@ impl QRead for TimeRange { match (start, end) { (Some(start), Some(end)) => { if start > end { - return Err(ParsingError::InvalidValue) + return Err(ParsingError::InvalidValue); } Ok(TimeRange::FullRange(start, end)) - }, + } (Some(start), None) => Ok(TimeRange::OnlyStart(start)), (None, Some(end)) => Ok(TimeRange::OnlyEnd(end)), (None, None) => Err(ParsingError::MissingAttribute), @@ -771,8 +950,11 @@ impl QRead for TimeRange { impl QRead for CalProp { async fn qread(xml: &mut Reader) -> Result { - xml.open(CAL_URN, "prop").await?; - let name = ComponentProperty(xml.prev_attr("name").ok_or(ParsingError::MissingAttribute)?); + xml.open(CAL_URN, "prop").await?; + let name = ComponentProperty( + xml.prev_attr("name") + .ok_or(ParsingError::MissingAttribute)?, + ); let novalue = xml.prev_attr("novalue").map(|v| v == "yes"); xml.close().await?; Ok(Self { name, novalue }) @@ -782,21 +964,23 @@ impl QRead for CalProp { #[cfg(test)] mod tests { use super::*; - use chrono::{Utc, TimeZone}; use crate::realization::Calendar; use crate::xml::Node; + use chrono::{TimeZone, Utc}; //use quick_reader::NsReader; async fn deserialize>(src: &str) -> T { - let mut rdr = Reader::new(quick_xml::NsReader::from_reader(src.as_bytes())).await.unwrap(); + let mut rdr = Reader::new(quick_xml::NsReader::from_reader(src.as_bytes())) + .await + .unwrap(); rdr.find().await.unwrap() } #[tokio::test] async fn basic_mkcalendar() { - let expected = MkCalendar(dav::Set(dav::PropValue(vec![ - dav::Property::DisplayName("Lisa's Events".into()), - ]))); + let expected = MkCalendar(dav::Set(dav::PropValue(vec![dav::Property::DisplayName( + "Lisa's Events".into(), + )]))); let src = r#" @@ -856,61 +1040,89 @@ END:VCALENDAR]]> let expected = CalendarQuery { selector: Some(CalendarSelector::Prop(dav::PropName(vec![ dav::PropertyRequest::GetEtag, - dav::PropertyRequest::Extension(PropertyRequest::CalendarData(CalendarDataRequest { - mime: None, - comp: Some(Comp { - name: Component::VCalendar, - prop_kind: Some(PropKind::Prop(vec![ - CalProp { + dav::PropertyRequest::Extension(PropertyRequest::CalendarData( + CalendarDataRequest { + mime: None, + comp: Some(Comp { + name: Component::VCalendar, + prop_kind: Some(PropKind::Prop(vec![CalProp { name: ComponentProperty("VERSION".into()), novalue: None, - } - ])), - comp_kind: Some(CompKind::Comp(vec![ - Comp { - name: Component::VEvent, - prop_kind: Some(PropKind::Prop(vec![ - CalProp { name: ComponentProperty("SUMMARY".into()), novalue: None }, - CalProp { name: ComponentProperty("UID".into()), novalue: None }, - CalProp { name: ComponentProperty("DTSTART".into()), novalue: None }, - CalProp { name: ComponentProperty("DTEND".into()), novalue: None }, - CalProp { name: ComponentProperty("DURATION".into()), novalue: None }, - CalProp { name: ComponentProperty("RRULE".into()), novalue: None }, - CalProp { name: ComponentProperty("RDATE".into()), novalue: None }, - CalProp { name: ComponentProperty("EXRULE".into()), novalue: None }, - CalProp { name: ComponentProperty("EXDATE".into()), novalue: None }, - CalProp { name: ComponentProperty("RECURRENCE-ID".into()), novalue: None }, - ])), - comp_kind: None, - }, - Comp { - name: Component::VTimeZone, - prop_kind: None, - comp_kind: None, - } - ])), - }), - recurrence: None, - limit_freebusy_set: None, - })), + }])), + comp_kind: Some(CompKind::Comp(vec![ + Comp { + name: Component::VEvent, + prop_kind: Some(PropKind::Prop(vec![ + CalProp { + name: ComponentProperty("SUMMARY".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("UID".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("DTSTART".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("DTEND".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("DURATION".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("RRULE".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("RDATE".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("EXRULE".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("EXDATE".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("RECURRENCE-ID".into()), + novalue: None, + }, + ])), + comp_kind: None, + }, + Comp { + name: Component::VTimeZone, + prop_kind: None, + comp_kind: None, + }, + ])), + }), + recurrence: None, + limit_freebusy_set: None, + }, + )), ]))), filter: Filter(CompFilter { name: Component::VCalendar, additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { prop_filter: vec![], - comp_filter: vec![ - CompFilter { - name: Component::VEvent, - additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { - prop_filter: vec![], - comp_filter: vec![], - time_range: Some(TimeRange::FullRange( - Utc.with_ymd_and_hms(2006, 1, 4, 0, 0, 0).unwrap(), - Utc.with_ymd_and_hms(2006, 1, 5, 0, 0, 0).unwrap(), - )), - })), - }, - ], + comp_filter: vec![CompFilter { + name: Component::VEvent, + additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { + prop_filter: vec![], + comp_filter: vec![], + time_range: Some(TimeRange::FullRange( + Utc.with_ymd_and_hms(2006, 1, 4, 0, 0, 0).unwrap(), + Utc.with_ymd_and_hms(2006, 1, 5, 0, 0, 0).unwrap(), + )), + })), + }], time_range: None, })), }), @@ -958,26 +1170,28 @@ END:VCALENDAR]]> } #[tokio::test] - async fn rfc_calendar_query_res() { + async fn rfc_calendar_query_res() { let expected = dav::Multistatus:: { responses: vec![ dav::Response { status_or_propstat: dav::StatusOrPropstat::PropStat( dav::Href("http://cal.example.com/bernard/work/abcd2.ics".into()), - vec![ - dav::PropStat { - prop: dav::AnyProp(vec![ - dav::AnyProperty::Value(dav::Property::GetEtag("\"fffff-abcd2\"".into())), - dav::AnyProperty::Value(dav::Property::Extension(Property::CalendarData(CalendarDataPayload { + vec![dav::PropStat { + prop: dav::AnyProp(vec![ + dav::AnyProperty::Value(dav::Property::GetEtag( + "\"fffff-abcd2\"".into(), + )), + dav::AnyProperty::Value(dav::Property::Extension( + Property::CalendarData(CalendarDataPayload { mime: None, payload: "BEGIN:VCALENDAR".into(), - }))), - ]), - status: dav::Status(http::status::StatusCode::OK), - error: None, - responsedescription: None, - }, - ], + }), + )), + ]), + status: dav::Status(http::status::StatusCode::OK), + error: None, + responsedescription: None, + }], ), error: None, location: None, @@ -986,20 +1200,22 @@ END:VCALENDAR]]> dav::Response { status_or_propstat: dav::StatusOrPropstat::PropStat( dav::Href("http://cal.example.com/bernard/work/abcd3.ics".into()), - vec![ - dav::PropStat { - prop: dav::AnyProp(vec![ - dav::AnyProperty::Value(dav::Property::GetEtag("\"fffff-abcd3\"".into())), - dav::AnyProperty::Value(dav::Property::Extension(Property::CalendarData(CalendarDataPayload { + vec![dav::PropStat { + prop: dav::AnyProp(vec![ + dav::AnyProperty::Value(dav::Property::GetEtag( + "\"fffff-abcd3\"".into(), + )), + dav::AnyProperty::Value(dav::Property::Extension( + Property::CalendarData(CalendarDataPayload { mime: None, payload: "BEGIN:VCALENDAR".into(), - }))), - ]), - status: dav::Status(http::status::StatusCode::OK), - error: None, - responsedescription: None, - }, - ], + }), + )), + ]), + status: dav::Status(http::status::StatusCode::OK), + error: None, + responsedescription: None, + }], ), error: None, location: None, @@ -1039,36 +1255,38 @@ END:VCALENDAR]]> } #[tokio::test] - async fn rfc_recurring_evt() { + async fn rfc_recurring_evt() { let expected = CalendarQuery:: { selector: Some(CalendarSelector::Prop(dav::PropName(vec![ - dav::PropertyRequest::Extension(PropertyRequest::CalendarData(CalendarDataRequest{ - mime: None, - comp: None, - recurrence: Some(RecurrenceModifier::LimitRecurrenceSet(LimitRecurrenceSet ( - Utc.with_ymd_and_hms(2006, 1, 3, 0, 0, 0).unwrap(), - Utc.with_ymd_and_hms(2006, 1, 5, 0, 0, 0).unwrap(), - ))), - limit_freebusy_set: None, - })), + dav::PropertyRequest::Extension(PropertyRequest::CalendarData( + CalendarDataRequest { + mime: None, + comp: None, + recurrence: Some(RecurrenceModifier::LimitRecurrenceSet( + LimitRecurrenceSet( + Utc.with_ymd_and_hms(2006, 1, 3, 0, 0, 0).unwrap(), + Utc.with_ymd_and_hms(2006, 1, 5, 0, 0, 0).unwrap(), + ), + )), + limit_freebusy_set: None, + }, + )), ]))), filter: Filter(CompFilter { name: Component::VCalendar, additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { prop_filter: vec![], - comp_filter: vec![ - CompFilter { - name: Component::VEvent, - additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { - prop_filter: vec![], - comp_filter: vec![], - time_range: Some(TimeRange::FullRange( - Utc.with_ymd_and_hms(2006, 1, 3, 0, 0, 0).unwrap(), - Utc.with_ymd_and_hms(2006, 1, 5, 0, 0, 0).unwrap(), - )), - })), - }, - ], + comp_filter: vec![CompFilter { + name: Component::VEvent, + additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { + prop_filter: vec![], + comp_filter: vec![], + time_range: Some(TimeRange::FullRange( + Utc.with_ymd_and_hms(2006, 1, 3, 0, 0, 0).unwrap(), + Utc.with_ymd_and_hms(2006, 1, 5, 0, 0, 0).unwrap(), + )), + })), + }], time_range: None, })), }), @@ -1104,32 +1322,34 @@ END:VCALENDAR]]> let expected = CalendarQuery:: { selector: Some(CalendarSelector::Prop(dav::PropName(vec![ dav::PropertyRequest::GetEtag, - dav::PropertyRequest::Extension(PropertyRequest::CalendarData(CalendarDataRequest { - mime: None, - comp: None, - recurrence: None, - limit_freebusy_set: None, - })) + dav::PropertyRequest::Extension(PropertyRequest::CalendarData( + CalendarDataRequest { + mime: None, + comp: None, + recurrence: None, + limit_freebusy_set: None, + }, + )), ]))), filter: Filter(CompFilter { name: Component::VCalendar, additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { time_range: None, prop_filter: vec![], - comp_filter: vec![ - CompFilter { - name: Component::VTodo, - additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { - time_range: None, - comp_filter: vec![], - prop_filter: vec![ - PropFilter { - name: ComponentProperty("COMPLETED".into()), - additional_rules: Some(PropFilterRules::IsNotDefined), - }, - PropFilter { - name: ComponentProperty("STATUS".into()), - additional_rules: Some(PropFilterRules::Match(PropFilterMatch { + comp_filter: vec![CompFilter { + name: Component::VTodo, + additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { + time_range: None, + comp_filter: vec![], + prop_filter: vec![ + PropFilter { + name: ComponentProperty("COMPLETED".into()), + additional_rules: Some(PropFilterRules::IsNotDefined), + }, + PropFilter { + name: ComponentProperty("STATUS".into()), + additional_rules: Some(PropFilterRules::Match( + PropFilterMatch { time_range: None, param_filter: vec![], time_or_text: Some(TimeOrText::Text(TextMatch { @@ -1137,12 +1357,12 @@ END:VCALENDAR]]> negate_condition: Some(true), text: "CANCELLED".into(), })), - })), - }, - ], - })), - } - ], + }, + )), + }, + ], + })), + }], })), }), timezone: None, @@ -1169,9 +1389,7 @@ END:VCALENDAR]]> "#; - let got = deserialize::>(src).await; assert_eq!(got, expected) - } } diff --git a/aero-dav/src/calencoder.rs b/aero-dav/src/calencoder.rs index d324c7f..06cafd4 100644 --- a/aero-dav/src/calencoder.rs +++ b/aero-dav/src/calencoder.rs @@ -1,10 +1,9 @@ +use quick_xml::events::{BytesText, Event}; use quick_xml::Error as QError; -use quick_xml::events::{Event, BytesText}; use super::caltypes::*; -use super::xml::{Node, QWrite, IWrite, Writer}; use super::types::Extension; - +use super::xml::{IWrite, Node, QWrite, Writer}; // ==================== Calendar Types Serialization ========================= @@ -54,7 +53,7 @@ impl QWrite for CalendarQuery { selector.qwrite(xml).await?; } self.filter.qwrite(xml).await?; - if let Some(tz) = &self.timezone { + if let Some(tz) = &self.timezone { tz.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await @@ -106,8 +105,8 @@ impl QWrite for PropertyRequest { Self::MinDateTime => atom("min-date-time").await, Self::MaxDateTime => atom("max-date-time").await, Self::MaxInstances => atom("max-instances").await, - Self::MaxAttendeesPerInstance => atom("max-attendees-per-instance").await, - Self::SupportedCollationSet => atom("supported-collation-set").await, + Self::MaxAttendeesPerInstance => atom("max-attendees-per-instance").await, + Self::SupportedCollationSet => atom("supported-collation-set").await, Self::CalendarData(req) => req.qwrite(xml).await, } } @@ -130,17 +129,21 @@ impl QWrite for Property { let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; - xml.q.write_event_async(Event::Text(BytesText::new(text))).await?; + xml.q + .write_event_async(Event::Text(BytesText::new(text))) + .await?; xml.q.write_event_async(Event::End(end)).await - }, + } Self::CalendarTimezone(payload) => { let start = xml.create_cal_element("calendar-timezone"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; - xml.q.write_event_async(Event::Text(BytesText::new(payload))).await?; + xml.q + .write_event_async(Event::Text(BytesText::new(payload))) + .await?; xml.q.write_event_async(Event::End(end)).await - }, + } Self::SupportedCalendarComponentSet(many_comp) => { let start = xml.create_cal_element("supported-calendar-component-set"); let end = start.to_end(); @@ -150,7 +153,7 @@ impl QWrite for Property { comp.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await - }, + } Self::SupportedCalendarData(many_mime) => { let start = xml.create_cal_element("supported-calendar-data"); let end = start.to_end(); @@ -160,49 +163,59 @@ impl QWrite for Property { mime.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await - }, + } Self::MaxResourceSize(bytes) => { let start = xml.create_cal_element("max-resource-size"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; - xml.q.write_event_async(Event::Text(BytesText::new(bytes.to_string().as_str()))).await?; + xml.q + .write_event_async(Event::Text(BytesText::new(bytes.to_string().as_str()))) + .await?; xml.q.write_event_async(Event::End(end)).await - }, + } Self::MinDateTime(dt) => { let start = xml.create_cal_element("min-date-time"); let end = start.to_end(); let dtstr = format!("{}", dt.format(ICAL_DATETIME_FMT)); xml.q.write_event_async(Event::Start(start.clone())).await?; - xml.q.write_event_async(Event::Text(BytesText::new(dtstr.as_str()))).await?; + xml.q + .write_event_async(Event::Text(BytesText::new(dtstr.as_str()))) + .await?; xml.q.write_event_async(Event::End(end)).await - }, + } Self::MaxDateTime(dt) => { let start = xml.create_cal_element("max-date-time"); let end = start.to_end(); let dtstr = format!("{}", dt.format(ICAL_DATETIME_FMT)); xml.q.write_event_async(Event::Start(start.clone())).await?; - xml.q.write_event_async(Event::Text(BytesText::new(dtstr.as_str()))).await?; + xml.q + .write_event_async(Event::Text(BytesText::new(dtstr.as_str()))) + .await?; xml.q.write_event_async(Event::End(end)).await - }, + } Self::MaxInstances(count) => { let start = xml.create_cal_element("max-instances"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; - xml.q.write_event_async(Event::Text(BytesText::new(count.to_string().as_str()))).await?; + xml.q + .write_event_async(Event::Text(BytesText::new(count.to_string().as_str()))) + .await?; xml.q.write_event_async(Event::End(end)).await - }, + } Self::MaxAttendeesPerInstance(count) => { let start = xml.create_cal_element("max-attendees-per-instance"); let end = start.to_end(); xml.q.write_event_async(Event::Start(start.clone())).await?; - xml.q.write_event_async(Event::Text(BytesText::new(count.to_string().as_str()))).await?; + xml.q + .write_event_async(Event::Text(BytesText::new(count.to_string().as_str()))) + .await?; xml.q.write_event_async(Event::End(end)).await - }, + } Self::SupportedCollationSet(many_collations) => { let start = xml.create_cal_element("supported-collation-set"); let end = start.to_end(); @@ -211,8 +224,8 @@ impl QWrite for Property { for collation in many_collations.iter() { collation.qwrite(xml).await?; } - xml.q.write_event_async(Event::End(end)).await - }, + xml.q.write_event_async(Event::End(end)).await + } Self::CalendarData(inner) => inner.qwrite(xml).await, } } @@ -225,7 +238,7 @@ impl QWrite for ResourceType { Self::Calendar => { let empty_tag = xml.create_cal_element("calendar"); xml.q.write_event_async(Event::Empty(empty_tag)).await - }, + } } } } @@ -245,7 +258,7 @@ impl QWrite for Violation { Self::NeedPrivileges => { let empty_tag = xml.create_dav_element("need-privileges"); xml.q.write_event_async(Event::Empty(empty_tag)).await - }, + } // Regular CalDAV errors Self::ResourceMustBeNull => atom("resource-must-be-null").await, @@ -262,7 +275,7 @@ impl QWrite for Violation { xml.q.write_event_async(Event::Start(start.clone())).await?; href.qwrite(xml).await?; xml.q.write_event_async(Event::End(end)).await - }, + } Self::MaxResourceSize => atom("max-resource-size").await, Self::MinDateTime => atom("min-date-time").await, Self::MaxDateTime => atom("max-date-time").await, @@ -284,13 +297,12 @@ impl QWrite for Violation { param_item.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await - }, + } Self::NumberOfMatchesWithinLimits => atom("number-of-matches-within-limits").await, } } } - // ---------------------------- Inner XML ------------------------------------ impl QWrite for SupportedCollation { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { @@ -300,19 +312,20 @@ impl QWrite for SupportedCollation { 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 Collation { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let col = match self { - Self::AsciiCaseMap => "i;ascii-casemap", - Self::Octet => "i;octet", - Self::Unknown(v) => v.as_str(), + Self::AsciiCaseMap => "i;ascii-casemap", + Self::Octet => "i;octet", + Self::Unknown(v) => v.as_str(), }; - xml.q.write_event_async(Event::Text(BytesText::new(col))).await + xml.q + .write_event_async(Event::Text(BytesText::new(col))) + .await } } @@ -332,7 +345,9 @@ impl QWrite for CalendarDataPayload { 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.payload.as_str()))).await?; + xml.q + .write_event_async(Event::Text(BytesText::new(self.payload.as_str()))) + .await?; xml.q.write_event_async(Event::End(end)).await } } @@ -347,7 +362,7 @@ impl QWrite for CalendarDataRequest { // Empty tag if self.comp.is_none() && self.recurrence.is_none() && self.limit_freebusy_set.is_none() { - return xml.q.write_event_async(Event::Empty(start.clone())).await + return xml.q.write_event_async(Event::Empty(start.clone())).await; } let end = start.to_end(); @@ -392,7 +407,7 @@ impl QWrite for Comp { comp_kind.qwrite(xml).await?; } xml.q.write_event_async(Event::End(end)).await - }, + } } } } @@ -411,7 +426,7 @@ impl QWrite for CompKind { Self::AllComp => { let empty_tag = xml.create_cal_element("allcomp"); xml.q.write_event_async(Event::Empty(empty_tag)).await - }, + } Self::Comp(many_comp) => { for comp in many_comp.iter() { // Required: recursion in an async fn requires boxing @@ -420,7 +435,10 @@ impl QWrite for CompKind { // For more information about this error, try `rustc --explain E0391`. // https://github.com/rust-lang/rust/issues/78649 #[inline(always)] - fn recurse<'a>(comp: &'a Comp, xml: &'a mut Writer) -> futures::future::BoxFuture<'a, Result<(), QError>> { + fn recurse<'a>( + comp: &'a Comp, + xml: &'a mut Writer, + ) -> futures::future::BoxFuture<'a, Result<(), QError>> { Box::pin(comp.qwrite(xml)) } recurse(comp, xml).await?; @@ -437,7 +455,7 @@ impl QWrite for PropKind { Self::AllProp => { let empty_tag = xml.create_cal_element("allprop"); xml.q.write_event_async(Event::Empty(empty_tag)).await - }, + } Self::Prop(many_prop) => { for prop in many_prop.iter() { prop.qwrite(xml).await?; @@ -473,8 +491,14 @@ impl QWrite for RecurrenceModifier { impl QWrite for Expand { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let mut empty = xml.create_cal_element("expand"); - empty.push_attribute(("start", format!("{}", self.0.format(ICAL_DATETIME_FMT)).as_str())); - empty.push_attribute(("end", format!("{}", self.1.format(ICAL_DATETIME_FMT)).as_str())); + empty.push_attribute(( + "start", + format!("{}", self.0.format(ICAL_DATETIME_FMT)).as_str(), + )); + empty.push_attribute(( + "end", + format!("{}", self.1.format(ICAL_DATETIME_FMT)).as_str(), + )); xml.q.write_event_async(Event::Empty(empty)).await } } @@ -482,8 +506,14 @@ impl QWrite for Expand { impl QWrite for LimitRecurrenceSet { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let mut empty = xml.create_cal_element("limit-recurrence-set"); - empty.push_attribute(("start", format!("{}", self.0.format(ICAL_DATETIME_FMT)).as_str())); - empty.push_attribute(("end", format!("{}", self.1.format(ICAL_DATETIME_FMT)).as_str())); + empty.push_attribute(( + "start", + format!("{}", self.0.format(ICAL_DATETIME_FMT)).as_str(), + )); + empty.push_attribute(( + "end", + format!("{}", self.1.format(ICAL_DATETIME_FMT)).as_str(), + )); xml.q.write_event_async(Event::Empty(empty)).await } } @@ -491,8 +521,14 @@ impl QWrite for LimitRecurrenceSet { impl QWrite for LimitFreebusySet { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let mut empty = xml.create_cal_element("limit-freebusy-set"); - empty.push_attribute(("start", format!("{}", self.0.format(ICAL_DATETIME_FMT)).as_str())); - empty.push_attribute(("end", format!("{}", self.1.format(ICAL_DATETIME_FMT)).as_str())); + empty.push_attribute(( + "start", + format!("{}", self.0.format(ICAL_DATETIME_FMT)).as_str(), + )); + empty.push_attribute(( + "end", + format!("{}", self.1.format(ICAL_DATETIME_FMT)).as_str(), + )); xml.q.write_event_async(Event::Empty(empty)).await } } @@ -503,11 +539,11 @@ impl QWrite for CalendarSelector { Self::AllProp => { let empty_tag = xml.create_dav_element("allprop"); xml.q.write_event_async(Event::Empty(empty_tag)).await - }, + } Self::PropName => { let empty_tag = xml.create_dav_element("propname"); xml.q.write_event_async(Event::Empty(empty_tag)).await - }, + } Self::Prop(prop) => prop.qwrite(xml).await, } } @@ -534,10 +570,10 @@ impl QWrite for CompFilter { impl QWrite for CompFilterRules { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { match self { - Self::IsNotDefined => { + Self::IsNotDefined => { let empty_tag = xml.create_dav_element("is-not-defined"); xml.q.write_event_async(Event::Empty(empty_tag)).await - }, + } Self::Matches(cfm) => cfm.qwrite(xml).await, } } @@ -559,7 +595,10 @@ impl QWrite for CompFilterMatch { // For more information about this error, try `rustc --explain E0391`. // https://github.com/rust-lang/rust/issues/78649 #[inline(always)] - fn recurse<'a>(comp: &'a CompFilter, xml: &'a mut Writer) -> futures::future::BoxFuture<'a, Result<(), QError>> { + fn recurse<'a>( + comp: &'a CompFilter, + xml: &'a mut Writer, + ) -> futures::future::BoxFuture<'a, Result<(), QError>> { Box::pin(comp.qwrite(xml)) } recurse(comp_item, xml).await?; @@ -591,7 +630,7 @@ impl QWrite for PropFilterRules { Self::IsNotDefined => { let empty_tag = xml.create_dav_element("is-not-defined"); xml.q.write_event_async(Event::Empty(empty_tag)).await - }, + } Self::Match(prop_match) => prop_match.qwrite(xml).await, } } @@ -635,7 +674,9 @@ impl QWrite for TextMatch { 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.text.as_str()))).await?; + xml.q + .write_event_async(Event::Text(BytesText::new(self.text.as_str()))) + .await?; xml.q.write_event_async(Event::End(end)).await } } @@ -663,7 +704,7 @@ impl QWrite for ParamFilterMatch { Self::IsNotDefined => { let empty_tag = xml.create_dav_element("is-not-defined"); xml.q.write_event_async(Event::Empty(empty_tag)).await - }, + } Self::Match(tm) => tm.qwrite(xml).await, } } @@ -675,7 +716,9 @@ impl QWrite for TimeZone { 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.as_str()))).await?; + xml.q + .write_event_async(Event::Text(BytesText::new(self.0.as_str()))) + .await?; xml.q.write_event_async(Event::End(end)).await } } @@ -695,11 +738,20 @@ impl QWrite for TimeRange { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let mut empty = xml.create_cal_element("time-range"); match self { - Self::OnlyStart(start) => empty.push_attribute(("start", format!("{}", start.format(ICAL_DATETIME_FMT)).as_str())), - Self::OnlyEnd(end) => empty.push_attribute(("end", format!("{}", end.format(ICAL_DATETIME_FMT)).as_str())), + Self::OnlyStart(start) => empty.push_attribute(( + "start", + format!("{}", start.format(ICAL_DATETIME_FMT)).as_str(), + )), + Self::OnlyEnd(end) => { + empty.push_attribute(("end", format!("{}", end.format(ICAL_DATETIME_FMT)).as_str())) + } Self::FullRange(start, end) => { - empty.push_attribute(("start", format!("{}", start.format(ICAL_DATETIME_FMT)).as_str())); - empty.push_attribute(("end", format!("{}", end.format(ICAL_DATETIME_FMT)).as_str())); + empty.push_attribute(( + "start", + format!("{}", start.format(ICAL_DATETIME_FMT)).as_str(), + )); + empty + .push_attribute(("end", format!("{}", end.format(ICAL_DATETIME_FMT)).as_str())); } } xml.q.write_event_async(Event::Empty(empty)).await @@ -709,16 +761,16 @@ impl QWrite for TimeRange { #[cfg(test)] mod tests { use super::*; - use crate::types as dav; use crate::realization::Calendar; + use crate::types as dav; + use chrono::{TimeZone, Utc}; use tokio::io::AsyncWriteExt; - use chrono::{Utc,TimeZone}; 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![ + let ns_to_apply = vec![ ("xmlns:D".into(), "DAV:".into()), ("xmlns:C".into(), "urn:ietf:params:xml:ns:caldav".into()), ]; @@ -728,91 +780,120 @@ mod tests { tokio_buffer.flush().await.expect("tokio buffer flush"); let got = std::str::from_utf8(buffer.as_slice()).unwrap(); - return got.into() + return got.into(); } #[tokio::test] async fn basic_violation() { - let got = serialize( - &dav::Error::(vec![ - dav::Violation::Extension(Violation::ResourceMustBeNull), - ]) - ).await; + let got = serialize(&dav::Error::(vec![dav::Violation::Extension( + Violation::ResourceMustBeNull, + )])) + .await; let expected = r#" "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + assert_eq!( + &got, expected, + "\n---GOT---\n{got}\n---EXP---\n{expected}\n" + ); } #[tokio::test] async fn rfc_calendar_query1_req() { - let got = serialize( - &CalendarQuery:: { - selector: Some(CalendarSelector::Prop(dav::PropName(vec![ - dav::PropertyRequest::GetEtag, - dav::PropertyRequest::Extension(PropertyRequest::CalendarData(CalendarDataRequest { + let got = serialize(&CalendarQuery:: { + selector: Some(CalendarSelector::Prop(dav::PropName(vec![ + dav::PropertyRequest::GetEtag, + dav::PropertyRequest::Extension(PropertyRequest::CalendarData( + CalendarDataRequest { mime: None, comp: Some(Comp { name: Component::VCalendar, - prop_kind: Some(PropKind::Prop(vec![ - CalProp { - name: ComponentProperty("VERSION".into()), - novalue: None, - } - ])), + prop_kind: Some(PropKind::Prop(vec![CalProp { + name: ComponentProperty("VERSION".into()), + novalue: None, + }])), comp_kind: Some(CompKind::Comp(vec![ - Comp { - name: Component::VEvent, - prop_kind: Some(PropKind::Prop(vec![ - CalProp { name: ComponentProperty("SUMMARY".into()), novalue: None }, - CalProp { name: ComponentProperty("UID".into()), novalue: None }, - CalProp { name: ComponentProperty("DTSTART".into()), novalue: None }, - CalProp { name: ComponentProperty("DTEND".into()), novalue: None }, - CalProp { name: ComponentProperty("DURATION".into()), novalue: None }, - CalProp { name: ComponentProperty("RRULE".into()), novalue: None }, - CalProp { name: ComponentProperty("RDATE".into()), novalue: None }, - CalProp { name: ComponentProperty("EXRULE".into()), novalue: None }, - CalProp { name: ComponentProperty("EXDATE".into()), novalue: None }, - CalProp { name: ComponentProperty("RECURRENCE-ID".into()), novalue: None }, - ])), - comp_kind: None, - }, - Comp { - name: Component::VTimeZone, - prop_kind: None, - comp_kind: None, - } - ])), - }), + Comp { + name: Component::VEvent, + prop_kind: Some(PropKind::Prop(vec![ + CalProp { + name: ComponentProperty("SUMMARY".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("UID".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("DTSTART".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("DTEND".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("DURATION".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("RRULE".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("RDATE".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("EXRULE".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("EXDATE".into()), + novalue: None, + }, + CalProp { + name: ComponentProperty("RECURRENCE-ID".into()), + novalue: None, + }, + ])), + comp_kind: None, + }, + Comp { + name: Component::VTimeZone, + prop_kind: None, + comp_kind: None, + }, + ])), + }), recurrence: None, limit_freebusy_set: None, - })), - ]))), - filter: Filter(CompFilter { - name: Component::VCalendar, - additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { - time_range: None, - prop_filter: vec![], - comp_filter: vec![ - CompFilter { - name: Component::VEvent, - additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { - time_range: Some(TimeRange::FullRange( - Utc.with_ymd_and_hms(2006,1,4,0,0,0).unwrap(), - Utc.with_ymd_and_hms(2006,1,5,0,0,0).unwrap(), - )), - prop_filter: vec![], - comp_filter: vec![], - })), - }, - ], - })), - }), - timezone: None, - } - ).await; + }, + )), + ]))), + filter: Filter(CompFilter { + name: Component::VCalendar, + additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { + time_range: None, + prop_filter: vec![], + comp_filter: vec![CompFilter { + name: Component::VEvent, + additional_rules: Some(CompFilterRules::Matches(CompFilterMatch { + time_range: Some(TimeRange::FullRange( + Utc.with_ymd_and_hms(2006, 1, 4, 0, 0, 0).unwrap(), + Utc.with_ymd_and_hms(2006, 1, 5, 0, 0, 0).unwrap(), + )), + prop_filter: vec![], + comp_filter: vec![], + })), + }], + })), + }), + timezone: None, + }) + .await; let expected = r#" @@ -844,59 +925,69 @@ mod tests { "#; - - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + + assert_eq!( + &got, expected, + "\n---GOT---\n{got}\n---EXP---\n{expected}\n" + ); } #[tokio::test] async fn rfc_calendar_query1_res() { - let got = serialize( - &dav::Multistatus:: { - responses: vec![ - dav::Response { - status_or_propstat: dav::StatusOrPropstat::PropStat( - dav::Href("http://cal.example.com/bernard/work/abcd2.ics".into()), - vec![dav::PropStat { + let got = serialize(&dav::Multistatus:: { + responses: vec![ + dav::Response { + status_or_propstat: dav::StatusOrPropstat::PropStat( + dav::Href("http://cal.example.com/bernard/work/abcd2.ics".into()), + vec![dav::PropStat { prop: dav::AnyProp(vec![ - dav::AnyProperty::Value(dav::Property::GetEtag("\"fffff-abcd2\"".into())), - dav::AnyProperty::Value(dav::Property::Extension(Property::CalendarData(CalendarDataPayload { - mime: None, - payload: "PLACEHOLDER".into() - }))), + dav::AnyProperty::Value(dav::Property::GetEtag( + "\"fffff-abcd2\"".into(), + )), + dav::AnyProperty::Value(dav::Property::Extension( + Property::CalendarData(CalendarDataPayload { + mime: None, + payload: "PLACEHOLDER".into(), + }), + )), ]), status: dav::Status(http::status::StatusCode::OK), error: None, responsedescription: None, - }] - ), - location: None, - error: None, - responsedescription: None, - }, - dav::Response { - status_or_propstat: dav::StatusOrPropstat::PropStat( - dav::Href("http://cal.example.com/bernard/work/abcd3.ics".into()), - vec![dav::PropStat { + }], + ), + location: None, + error: None, + responsedescription: None, + }, + dav::Response { + status_or_propstat: dav::StatusOrPropstat::PropStat( + dav::Href("http://cal.example.com/bernard/work/abcd3.ics".into()), + vec![dav::PropStat { prop: dav::AnyProp(vec![ - dav::AnyProperty::Value(dav::Property::GetEtag("\"fffff-abcd3\"".into())), - dav::AnyProperty::Value(dav::Property::Extension(Property::CalendarData(CalendarDataPayload{ - mime: None, - payload: "PLACEHOLDER".into(), - }))), + dav::AnyProperty::Value(dav::Property::GetEtag( + "\"fffff-abcd3\"".into(), + )), + dav::AnyProperty::Value(dav::Property::Extension( + Property::CalendarData(CalendarDataPayload { + mime: None, + payload: "PLACEHOLDER".into(), + }), + )), ]), status: dav::Status(http::status::StatusCode::OK), error: None, responsedescription: None, - }] - ), - location: None, - error: None, - responsedescription: None, - }, - ], - responsedescription: None, - }, - ).await; + }], + ), + location: None, + error: None, + responsedescription: None, + }, + ], + responsedescription: None, + }) + .await; let expected = r#" @@ -921,7 +1012,9 @@ mod tests { "#; - - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + assert_eq!( + &got, expected, + "\n---GOT---\n{got}\n---EXP---\n{expected}\n" + ); } } diff --git a/aero-dav/src/caltypes.rs b/aero-dav/src/caltypes.rs index 08991a0..7c85642 100644 --- a/aero-dav/src/caltypes.rs +++ b/aero-dav/src/caltypes.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] -use chrono::{DateTime,Utc}; use super::types as dav; +use chrono::{DateTime, Utc}; pub const ICAL_DATETIME_FMT: &str = "%Y%m%dT%H%M%SZ"; @@ -13,7 +13,6 @@ pub const ICAL_DATETIME_FMT: &str = "%Y%m%dT%H%M%SZ"; // For reference, non-official extensions documented by SabreDAV: // https://github.com/apple/ccs-calendarserver/tree/master/doc/Extensions - // ----- Root elements ----- // --- (MKCALENDAR PART) --- @@ -33,17 +32,16 @@ pub const ICAL_DATETIME_FMT: &str = "%Y%m%dT%H%M%SZ"; #[derive(Debug, PartialEq, Clone)] pub struct MkCalendar(pub dav::Set); - /// If a response body for a successful request is included, it MUST /// be a CALDAV:mkcalendar-response XML element. /// /// /// /// ---- -/// +/// /// ANY is not satisfying, so looking at RFC5689 /// https://www.rfc-editor.org/rfc/rfc5689.html#section-5.2 -/// +/// /// Definition: /// /// @@ -63,9 +61,9 @@ pub enum Report { /// Namespace: urn:ietf:params:xml:ns:caldav /// /// Purpose: Defines a report for querying calendar object resources. -/// +/// /// Description: See Section 7.8. -/// +/// /// Definition: /// /// Calendrier de Mathilde Desruisseaux - CalendarDescription { - lang: Option, - text: String, - }, + CalendarDescription { lang: Option, text: String }, /// 5.2.2. CALDAV:calendar-timezone Property /// @@ -232,7 +227,7 @@ pub enum Property { /// sequence "]]>", which is the end delimiter for the CDATA section. /// /// Definition: - /// + /// /// ```xmlschema /// /// PCDATA value: an iCalendar object with exactly one VTIMEZONE component. @@ -630,7 +625,7 @@ pub enum Property { /// WebDAV property. However, the CALDAV:calendar-data XML element is /// not a WebDAV property and, as such, is not returned in PROPFIND /// responses, nor used in PROPPATCH requests. - /// + /// /// Note: The iCalendar data embedded within the CALDAV:calendar-data /// XML element MUST follow the standard XML character data encoding /// rules, including use of <, >, & etc. entity encoding or @@ -649,7 +644,7 @@ pub enum Violation { /// (CALDAV:calendar-collection-location-ok): The Request-URI MUST /// identify a location where a calendar collection can be created; CalendarCollectionLocationOk, - + /// (CALDAV:valid-calendar-data): The time zone specified in CALDAV: /// calendar-timezone property MUST be a valid iCalendar object /// containing a single valid VTIMEZONE component. @@ -712,7 +707,7 @@ pub enum Violation { /// date-time property value (Section 5.2.6) on the calendar /// collection where the resource will be stored; MinDateTime, - + /// (CALDAV:max-date-time): The resource submitted in the PUT request, /// or targeted by a COPY or MOVE request, MUST have all of its /// iCalendar DATE or DATE-TIME property values (for each recurring @@ -784,15 +779,15 @@ pub enum Violation { /// To deal with this, this specification makes use of the IANA Collation /// Registry defined in [RFC4790] to specify collations that may be used /// to carry out the text comparison operations with a well-defined rule. -/// +/// /// The comparisons used in CalDAV are all "substring" matches, as per /// [RFC4790], Section 4.2. Collations supported by the server MUST /// support "substring" match operations. -/// +/// /// CalDAV servers are REQUIRED to support the "i;ascii-casemap" and /// "i;octet" collations, as described in [RFC4790], and MAY support /// other collations. -/// +/// /// Servers MUST advertise the set of collations that they support via /// the CALDAV:supported-collation-set property defined on any resource /// that supports reports that use collations. @@ -807,7 +802,7 @@ pub enum Violation { /// /// Wildcards (as defined in [RFC4790], Section 3.2) MUST NOT be used in /// the collation identifier. -/// +/// /// If the client chooses a collation not supported by the server, the /// server MUST respond with a CALDAV:supported-collation precondition /// error response. @@ -915,7 +910,7 @@ pub struct CompSupport(pub Component); /// Description: The CALDAV:allcomp XML element can be used when the /// client wants all types of components returned by a calendaring /// REPORT request. -/// +/// /// Definition: /// /// @@ -997,7 +992,7 @@ pub enum RecurrenceModifier { /// recurrence set into calendar components that define exactly one /// recurrence instance, and MUST return only those whose scheduled /// time intersect a specified time range. -/// +/// /// The "start" attribute specifies the inclusive start of the time /// range, and the "end" attribute specifies the non-inclusive end of /// the time range. Both attributes are specified as date with UTC @@ -1189,7 +1184,7 @@ pub struct CompFilterMatch { /// Name: prop-filter /// /// Namespace: urn:ietf:params:xml:ns:caldav -/// +/// /// Purpose: Specifies search criteria on calendar properties. /// /// Description: The CALDAV:prop-filter XML element specifies a query @@ -1352,8 +1347,6 @@ pub enum ParamFilterMatch { /// /* CURRENTLY INLINED */ - - /// Name: timezone /// /// Namespace: urn:ietf:params:xml:ns:caldav @@ -1475,7 +1468,7 @@ impl PropertyParameter { } } -#[derive(Default,Debug,PartialEq,Clone)] +#[derive(Default, Debug, PartialEq, Clone)] pub enum Collation { #[default] AsciiCaseMap, @@ -1492,9 +1485,9 @@ impl Collation { } pub fn new(v: String) -> Self { match v.as_str() { - "i;ascii-casemap" => Self::AsciiCaseMap, - "i;octet" => Self::Octet, - _ => Self::Unknown(v), + "i;ascii-casemap" => Self::AsciiCaseMap, + "i;octet" => Self::Octet, + _ => Self::Unknown(v), } } } 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, + } + ); } - } diff --git a/aero-dav/src/encoder.rs b/aero-dav/src/encoder.rs index 1320c8a..fd87e95 100644 --- a/aero-dav/src/encoder.rs +++ b/aero-dav/src/encoder.rs @@ -1,8 +1,7 @@ -use quick_xml::Error as QError; -use quick_xml::events::{Event, BytesText}; use super::types::*; -use super::xml::{Node, Writer,QWrite,IWrite}; - +use super::xml::{IWrite, Node, QWrite, Writer}; +use quick_xml::events::{BytesText, Event}; +use quick_xml::Error as QError; // --- XML ROOTS @@ -16,15 +15,17 @@ impl QWrite for PropFind { match self { Self::PropName => { let empty_propname = xml.create_dav_element("propname"); - xml.q.write_event_async(Event::Empty(empty_propname)).await? - }, + 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 @@ -45,9 +46,8 @@ impl QWrite for PropertyUpdate { } } - /// PROPFIND RESPONSE, PROPPATCH RESPONSE, COPY RESPONSE, MOVE RESPONSE -/// DELETE RESPONSE, +/// DELETE RESPONSE, impl QWrite for Multistatus { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("multistatus"); @@ -140,7 +140,6 @@ impl QWrite for Remove { } } - impl QWrite for PropName { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("prop"); @@ -176,14 +175,15 @@ impl QWrite for AnyProperty { } } - 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::Text(BytesText::new(&self.0))) + .await?; xml.q.write_event_async(Event::End(end)).await } } @@ -216,9 +216,9 @@ impl QWrite for StatusOrPropstat { href.qwrite(xml).await?; } status.qwrite(xml).await - }, + } Self::PropStat(href, propstat_list) => { - href.qwrite(xml).await?; + href.qwrite(xml).await?; for propstat in propstat_list.iter() { propstat.qwrite(xml).await?; } @@ -235,8 +235,14 @@ impl QWrite for Status { 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?; + 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?; @@ -250,7 +256,9 @@ impl QWrite for 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::Text(BytesText::new(&self.0))) + .await?; xml.q.write_event_async(Event::End(end)).await } } @@ -296,62 +304,76 @@ impl QWrite for Property { 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::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::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::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::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::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::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::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"); @@ -362,17 +384,17 @@ impl QWrite for Property { 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?; @@ -384,7 +406,7 @@ impl QWrite for Property { } xml.q.write_event_async(Event::End(end)).await?; } - }, + } SupportedLock(many_entries) => { // @@ -401,7 +423,7 @@ impl QWrite for Property { } xml.q.write_event_async(Event::End(end)).await?; } - }, + } Extension(inner) => inner.qwrite(xml).await?, }; Ok(()) @@ -413,8 +435,10 @@ impl QWrite for ResourceType { match self { Self::Collection => { let empty_collection = xml.create_dav_element("collection"); - xml.q.write_event_async(Event::Empty(empty_collection)).await - }, + xml.q + .write_event_async(Event::Empty(empty_collection)) + .await + } Self::Extension(inner) => inner.qwrite(xml).await, } } @@ -425,7 +449,7 @@ impl QWrite for Include { let start = xml.create_dav_element("include"); let end = start.to_end(); - xml.q.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for prop in self.0.iter() { prop.qwrite(xml).await?; } @@ -505,8 +529,8 @@ impl QWrite for LockType { 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 } } @@ -521,12 +545,12 @@ impl QWrite for LockScope { 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 } } @@ -538,7 +562,11 @@ impl QWrite for Owner { 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::Txt(txt) => { + xml.q + .write_event_async(Event::Text(BytesText::new(&txt))) + .await? + } Self::Href(href) => href.qwrite(xml).await?, Self::Unknown => (), } @@ -553,9 +581,21 @@ impl QWrite for Depth { 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?, + 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 } @@ -570,9 +610,15 @@ impl QWrite for Timeout { 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::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 } @@ -620,8 +666,10 @@ impl QWrite for Violation { }; match self { - Violation::LockTokenMatchesRequestUri => atom("lock-token-matches-request-uri").await, - Violation::LockTokenSubmitted(hrefs) if hrefs.is_empty() => atom("lock-token-submitted").await, + 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(); @@ -631,8 +679,10 @@ impl QWrite for Violation { 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) 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(); @@ -642,11 +692,13 @@ impl QWrite for Violation { 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::CannotModifyProtectedProperty => { + atom("cannot-modify-protected-property").await + } Violation::Extension(inner) => inner.qwrite(xml).await, } } @@ -654,30 +706,32 @@ impl QWrite for Violation { #[cfg(test)] mod tests { - use super::*; 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 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() + 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(); + let mut rdr = xml::Reader::new(quick_xml::reader::NsReader::from_reader(src.as_bytes())) + .await + .unwrap(); rdr.find().await.unwrap() } @@ -688,15 +742,18 @@ mod tests { 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!( + &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 orig = Multistatus:: { + responses: vec![], + responsedescription: Some(ResponseDescription("Hello world".into())), }; let got = serialize(&orig).await; @@ -704,18 +761,18 @@ mod tests { Hello world "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + 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 orig = Error::(vec![Violation::LockTokenSubmitted(vec![Href( + "/locked/".into(), + )])]); let got = serialize(&orig).await; let expected = r#" @@ -724,7 +781,10 @@ mod tests { "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + assert_eq!( + &got, expected, + "\n---GOT---\n{got}\n---EXP---\n{expected}\n" + ); assert_eq!(deserialize::>(got.as_str()).await, orig) } @@ -738,7 +798,10 @@ mod tests { "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + assert_eq!( + &got, expected, + "\n---GOT---\n{got}\n---EXP---\n{expected}\n" + ); assert_eq!(deserialize::>(got.as_str()).await, orig) } @@ -759,7 +822,7 @@ mod tests { status: Status(http::status::StatusCode::OK), error: None, responsedescription: None, - }] + }], ), error: None, responsedescription: None, @@ -782,8 +845,8 @@ mod tests { status: Status(http::status::StatusCode::OK), error: None, responsedescription: None, - } - ]), + }], + ), error: None, responsedescription: None, location: None, @@ -825,8 +888,10 @@ mod tests { "#; - - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + assert_eq!( + &got, expected, + "\n---GOT---\n{got}\n---EXP---\n{expected}\n" + ); assert_eq!(deserialize::>(got.as_str()).await, orig) } @@ -835,17 +900,20 @@ mod tests { let orig = PropFind::::AllProp(None); let got = serialize(&orig).await; - let expected = r#" + let expected = r#" "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + 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}; + use chrono::{FixedOffset, TimeZone}; let orig = Multistatus:: { responses: vec![ @@ -853,28 +921,34 @@ mod tests { 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, - }] + 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, @@ -884,37 +958,43 @@ mod tests { 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, - }] + 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, @@ -993,15 +1073,18 @@ mod tests { "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + 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, + PropertyRequest::DisplayName, + PropertyRequest::ResourceType, ]))); let got = serialize(&orig).await; @@ -1014,19 +1097,20 @@ mod tests { "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + 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, - ]))), + PropertyUpdateItem::Set(Set(PropValue(vec![Property::GetContentLanguage( + "fr-FR".into(), + )]))), + PropertyUpdateItem::Remove(Remove(PropName(vec![PropertyRequest::DisplayName]))), ]); let got = serialize(&orig).await; @@ -1043,8 +1127,14 @@ mod tests { "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); - assert_eq!(deserialize::>(got.as_str()).await, orig) + assert_eq!( + &got, expected, + "\n---GOT---\n{got}\n---EXP---\n{expected}\n" + ); + assert_eq!( + deserialize::>(got.as_str()).await, + orig + ) } #[tokio::test] @@ -1053,7 +1143,7 @@ mod tests { 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()) + Status(http::status::StatusCode::from_u16(423).unwrap()), ), error: Some(Error(vec![Violation::LockTokenSubmitted(vec![])])), responsedescription: None, @@ -1074,7 +1164,10 @@ mod tests { "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + assert_eq!( + &got, expected, + "\n---GOT---\n{got}\n---EXP---\n{expected}\n" + ); assert_eq!(deserialize::>(got.as_str()).await, orig) } @@ -1083,7 +1176,9 @@ mod tests { let orig = LockInfo { lockscope: LockScope::Exclusive, locktype: LockType::Write, - owner: Some(Owner::Href(Href("http://example.org/~ejw/contact.html".into()))), + owner: Some(Owner::Href(Href( + "http://example.org/~ejw/contact.html".into(), + ))), }; let got = serialize(&orig).await; @@ -1100,23 +1195,30 @@ mod tests { "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + 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 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; @@ -1144,7 +1246,10 @@ mod tests { "#; - assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); + assert_eq!( + &got, expected, + "\n---GOT---\n{got}\n---EXP---\n{expected}\n" + ); assert_eq!(deserialize::>(got.as_str()).await, orig) } } diff --git a/aero-dav/src/error.rs b/aero-dav/src/error.rs index 570f779..c8f1de1 100644 --- a/aero-dav/src/error.rs +++ b/aero-dav/src/error.rs @@ -10,10 +10,10 @@ pub enum ParsingError { TagNotFound, InvalidValue, Utf8Error(std::str::Utf8Error), - QuickXml(quick_xml::Error), + QuickXml(quick_xml::Error), Chrono(chrono::format::ParseError), Int(std::num::ParseIntError), - Eof + Eof, } impl std::fmt::Display for ParsingError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/aero-dav/src/lib.rs b/aero-dav/src/lib.rs index 009951a..7507ddc 100644 --- a/aero-dav/src/lib.rs +++ b/aero-dav/src/lib.rs @@ -7,19 +7,19 @@ pub mod error; pub mod xml; // webdav -pub mod types; -pub mod encoder; pub mod decoder; +pub mod encoder; +pub mod types; // calendar -pub mod caltypes; -pub mod calencoder; pub mod caldecoder; +pub mod calencoder; +pub mod caltypes; // acl (wip) -pub mod acltypes; -pub mod aclencoder; pub mod acldecoder; +pub mod aclencoder; +pub mod acltypes; // versioning (wip) mod versioningtypes; diff --git a/aero-dav/src/realization.rs b/aero-dav/src/realization.rs index bfed4d7..7283e68 100644 --- a/aero-dav/src/realization.rs +++ b/aero-dav/src/realization.rs @@ -1,8 +1,8 @@ -use super::types as dav; -use super::caltypes as cal; use super::acltypes as acl; -use super::xml; +use super::caltypes as cal; use super::error; +use super::types as dav; +use super::xml; #[derive(Debug, PartialEq, Clone)] pub struct Disabled(()); @@ -12,12 +12,15 @@ impl xml::QRead for Disabled { } } impl xml::QWrite for Disabled { - async fn qwrite(&self, _xml: &mut xml::Writer) -> Result<(), quick_xml::Error> { + async fn qwrite( + &self, + _xml: &mut xml::Writer, + ) -> Result<(), quick_xml::Error> { unreachable!() } } -/// The base WebDAV +/// The base WebDAV /// /// Any extension is disabled through an object we can't build /// due to a private inner element. @@ -33,8 +36,7 @@ impl dav::Extension for Core { // WebDAV with the base Calendar implementation (RFC4791) #[derive(Debug, PartialEq, Clone)] pub struct Calendar {} -impl dav::Extension for Calendar -{ +impl dav::Extension for Calendar { type Error = cal::Violation; type Property = cal::Property; type PropertyRequest = cal::PropertyRequest; @@ -44,8 +46,7 @@ impl dav::Extension for Calendar // ACL #[derive(Debug, PartialEq, Clone)] pub struct Acl {} -impl dav::Extension for Acl -{ +impl dav::Extension for Acl { type Error = Disabled; type Property = acl::Property; type PropertyRequest = acl::PropertyRequest; @@ -77,7 +78,10 @@ impl xml::QRead for Property { } } impl xml::QWrite for Property { - async fn qwrite(&self, xml: &mut xml::Writer) -> Result<(), quick_xml::Error> { + async fn qwrite( + &self, + xml: &mut xml::Writer, + ) -> Result<(), quick_xml::Error> { match self { Self::Cal(c) => c.qwrite(xml).await, Self::Acl(a) => a.qwrite(xml).await, @@ -96,11 +100,16 @@ impl xml::QRead for PropertyRequest { Err(error::ParsingError::Recoverable) => (), otherwise => return otherwise.map(PropertyRequest::Cal), } - acl::PropertyRequest::qread(xml).await.map(PropertyRequest::Acl) + acl::PropertyRequest::qread(xml) + .await + .map(PropertyRequest::Acl) } } impl xml::QWrite for PropertyRequest { - async fn qwrite(&self, xml: &mut xml::Writer) -> Result<(), quick_xml::Error> { + async fn qwrite( + &self, + xml: &mut xml::Writer, + ) -> Result<(), quick_xml::Error> { match self { Self::Cal(c) => c.qwrite(xml).await, Self::Acl(a) => a.qwrite(xml).await, @@ -123,7 +132,10 @@ impl xml::QRead for ResourceType { } } impl xml::QWrite for ResourceType { - async fn qwrite(&self, xml: &mut xml::Writer) -> Result<(), quick_xml::Error> { + async fn qwrite( + &self, + xml: &mut xml::Writer, + ) -> Result<(), quick_xml::Error> { match self { Self::Cal(c) => c.qwrite(xml).await, Self::Acl(a) => a.qwrite(xml).await, diff --git a/aero-dav/src/types.rs b/aero-dav/src/types.rs index 9457a8f..d5466da 100644 --- a/aero-dav/src/types.rs +++ b/aero-dav/src/types.rs @@ -1,8 +1,8 @@ #![allow(dead_code)] use std::fmt::Debug; -use chrono::{DateTime,FixedOffset}; use super::xml; +use chrono::{DateTime, FixedOffset}; /// It's how we implement a DAV extension /// (That's the dark magic part...) @@ -42,7 +42,7 @@ pub struct ActiveLock { /// /// #[derive(Debug, PartialEq)] -pub struct Collection{} +pub struct Collection {} /// 14.4 depth XML Element /// @@ -58,7 +58,7 @@ pub struct Collection{} pub enum Depth { Zero, One, - Infinity + Infinity, } /// 14.5 error XML Element @@ -153,7 +153,6 @@ pub enum Violation { /// PROPFIND requests on collections. PropfindFiniteDepth, - /// Name: cannot-modify-protected-property /// /// Use with: 403 Forbidden @@ -172,7 +171,7 @@ pub enum Violation { /// Name: exclusive /// /// Purpose: Specifies an exclusive lock. -/// +/// /// #[derive(Debug, PartialEq)] pub struct Exclusive {} @@ -193,7 +192,6 @@ pub struct Exclusive {} #[derive(Debug, PartialEq, Clone)] pub struct Href(pub String); - /// 14.8. include XML Element /// /// Name: include @@ -280,7 +278,7 @@ pub struct LockRoot(pub Href); #[derive(Debug, PartialEq, Clone)] pub enum LockScope { Exclusive, - Shared + Shared, } /// 14.14. locktoken XML Element @@ -288,7 +286,7 @@ pub enum LockScope { /// Name: locktoken /// /// Purpose: The lock token associated with a lock. -/// +/// /// Description: The href contains a single lock token URI, which /// refers to the lock. /// @@ -314,7 +312,7 @@ pub enum LockType { /// /// /// - Write + Write, } /// 14.16. multistatus XML Element @@ -477,7 +475,6 @@ pub struct PropStat { pub responsedescription: Option, } - /// 14.23. remove XML Element /// /// Name: remove @@ -579,15 +576,14 @@ pub struct Set(pub PropValue); #[derive(Debug, PartialEq, Clone)] pub struct Shared {} - /// 14.28. status XML Element -/// +/// /// Name: status /// /// Purpose: Holds a single HTTP status-line. /// /// Value: status-line (defined in Section 6.1 of [RFC2616]) -/// +/// /// //@FIXME: Better typing is possible with an enum for example #[derive(Debug, PartialEq, Clone)] @@ -624,7 +620,6 @@ pub enum Timeout { Infinite, } - /// 15. DAV Properties /// /// For DAV properties, the name of the property is also the same as the @@ -704,7 +699,7 @@ pub enum Property { CreationDate(DateTime), /// 15.2. displayname Property - /// + /// /// Name: displayname /// /// Purpose: Provides a name for the resource that is suitable for @@ -734,7 +729,6 @@ pub enum Property { /// DisplayName(String), - /// 15.3. getcontentlanguage Property /// /// Name: getcontentlanguage @@ -893,7 +887,6 @@ pub enum Property { /// LockDiscovery(Vec), - /// 15.9. resourcetype Property /// /// Name: resourcetype @@ -920,7 +913,7 @@ pub enum Property { /// type. /// /// Example: (fictional example to show extensibility) - /// + /// /// /// /// diff --git a/aero-dav/src/xml.rs b/aero-dav/src/xml.rs index d57093e..c89f531 100644 --- a/aero-dav/src/xml.rs +++ b/aero-dav/src/xml.rs @@ -1,8 +1,8 @@ use futures::Future; -use quick_xml::events::{Event, BytesStart}; +use quick_xml::events::{BytesStart, Event}; use quick_xml::name::ResolveResult; use quick_xml::reader::NsReader; -use tokio::io::{AsyncWrite, AsyncBufRead}; +use tokio::io::{AsyncBufRead, AsyncWrite}; use super::error::ParsingError; @@ -17,7 +17,10 @@ pub trait IRead = AsyncBufRead + Unpin; // Serialization/Deserialization traits pub trait QWrite { - fn qwrite(&self, xml: &mut Writer) -> impl Future> + Send; + fn qwrite( + &self, + xml: &mut Writer, + ) -> impl Future> + Send; } pub trait QRead { fn qread(xml: &mut Reader) -> impl Future>; @@ -44,7 +47,11 @@ impl Writer { fn create_ns_element(&mut self, ns: &str, name: &str) -> BytesStart<'static> { let mut start = BytesStart::new(format!("{}:{}", ns, name)); if !self.ns_to_apply.is_empty() { - start.extend_attributes(self.ns_to_apply.iter().map(|(k, n)| (k.as_str(), n.as_str()))); + start.extend_attributes( + self.ns_to_apply + .iter() + .map(|(k, n)| (k.as_str(), n.as_str())), + ); self.ns_to_apply.clear() } start @@ -66,16 +73,26 @@ impl Reader { let parents = vec![]; let prev = Event::Eof; buf.clear(); - Ok(Self { cur, prev, parents, rdr, buf }) + Ok(Self { + cur, + prev, + parents, + rdr, + buf, + }) } /// read one more tag /// do not expose it publicly async fn next(&mut self) -> Result, ParsingError> { - let evt = self.rdr.read_event_into_async(&mut self.buf).await?.into_owned(); - self.buf.clear(); - self.prev = std::mem::replace(&mut self.cur, evt); - Ok(self.prev.clone()) + let evt = self + .rdr + .read_event_into_async(&mut self.buf) + .await? + .into_owned(); + self.buf.clear(); + self.prev = std::mem::replace(&mut self.cur, evt); + Ok(self.prev.clone()) } /// skip a node at current level @@ -84,9 +101,12 @@ impl Reader { //println!("skipping inside node {:?} value {:?}", self.parents.last(), self.cur); match &self.cur { Event::Start(b) => { - let _span = self.rdr.read_to_end_into_async(b.to_end().name(), &mut self.buf).await?; + let _span = self + .rdr + .read_to_end_into_async(b.to_end().name(), &mut self.buf) + .await?; self.next().await - }, + } Event::End(_) => Err(ParsingError::WrongToken), Event::Eof => Err(ParsingError::Eof), _ => self.next().await, @@ -100,13 +120,13 @@ impl Reader { Event::End(be) => be.name(), _ => return false, }; - + let (extr_ns, local) = self.rdr.resolve_element(qname); if local.into_inner() != key.as_bytes() { - return false + return false; } - + match extr_ns { ResolveResult::Bound(v) => v.into_inner() == ns, _ => false, @@ -142,7 +162,7 @@ impl Reader { Event::CData(unescaped) => { acc.push_str(std::str::from_utf8(unescaped.as_ref())?); self.next().await? - }, + } Event::Text(escaped) => { acc.push_str(escaped.unescape()?.as_ref()); self.next().await? @@ -153,33 +173,41 @@ impl Reader { } } - pub async fn maybe_read>(&mut self, t: &mut Option, dirty: &mut bool) -> Result<(), ParsingError> { + pub async fn maybe_read>( + &mut self, + t: &mut Option, + dirty: &mut bool, + ) -> Result<(), ParsingError> { if !self.parent_has_child() { - return Ok(()) + return Ok(()); } match N::qread(self).await { - Ok(v) => { - *t = Some(v); + Ok(v) => { + *t = Some(v); *dirty = true; - Ok(()) - }, + Ok(()) + } Err(ParsingError::Recoverable) => Ok(()), Err(e) => Err(e), } } - pub async fn maybe_push>(&mut self, t: &mut Vec, dirty: &mut bool) -> Result<(), ParsingError> { + pub async fn maybe_push>( + &mut self, + t: &mut Vec, + dirty: &mut bool, + ) -> Result<(), ParsingError> { if !self.parent_has_child() { - return Ok(()) + return Ok(()); } match N::qread(self).await { - Ok(v) => { - t.push(v); + Ok(v) => { + t.push(v); *dirty = true; - Ok(()) - }, + Ok(()) + } Err(ParsingError::Recoverable) => Ok(()), Err(e) => Err(e), } @@ -220,7 +248,7 @@ impl Reader { pub async fn collect>(&mut self) -> Result, ParsingError> { let mut acc = Vec::new(); if !self.parent_has_child() { - return Ok(acc) + return Ok(acc); } loop { @@ -229,7 +257,7 @@ impl Reader { Event::End(_) => return Ok(acc), _ => { self.skip().await?; - }, + } }, Ok(v) => acc.push(v), Err(e) => return Err(e), @@ -242,13 +270,13 @@ impl Reader { let evt = match self.peek() { Event::Empty(_) if self.is_tag(ns, key) => { // hack to make `prev_attr` works - // here we duplicate the current tag - // as in other words, we virtually moved one token + // here we duplicate the current tag + // as in other words, we virtually moved one token // which is useful for prev_attr and any logic based on // self.prev + self.open() on empty nodes self.prev = self.cur.clone(); self.cur.clone() - }, + } Event::Start(_) if self.is_tag(ns, key) => self.next().await?, _ => return Err(ParsingError::Recoverable), }; @@ -258,7 +286,11 @@ impl Reader { Ok(evt) } - pub async fn open_start(&mut self, ns: &[u8], key: &str) -> Result, ParsingError> { + pub async fn open_start( + &mut self, + ns: &[u8], + key: &str, + ) -> Result, ParsingError> { //println!("try open start tag {:?}, on {:?}", key, self.peek()); let evt = match self.peek() { Event::Start(_) if self.is_tag(ns, key) => self.next().await?, @@ -270,7 +302,11 @@ impl Reader { Ok(evt) } - pub async fn maybe_open(&mut self, ns: &[u8], key: &str) -> Result>, ParsingError> { + pub async fn maybe_open( + &mut self, + ns: &[u8], + key: &str, + ) -> Result>, ParsingError> { match self.open(ns, key).await { Ok(v) => Ok(Some(v)), Err(ParsingError::Recoverable) => Ok(None), @@ -278,7 +314,11 @@ impl Reader { } } - pub async fn maybe_open_start(&mut self, ns: &[u8], key: &str) -> Result>, ParsingError> { + pub async fn maybe_open_start( + &mut self, + ns: &[u8], + key: &str, + ) -> Result>, ParsingError> { match self.open_start(ns, key).await { Ok(v) => Ok(Some(v)), Err(ParsingError::Recoverable) => Ok(None), @@ -289,9 +329,12 @@ impl Reader { pub fn prev_attr(&self, attr: &str) -> Option { match &self.prev { Event::Start(bs) | Event::Empty(bs) => match bs.try_get_attribute(attr) { - Ok(Some(attr)) => attr.decode_and_unescape_value(&self.rdr).ok().map(|v| v.into_owned()), + Ok(Some(attr)) => attr + .decode_and_unescape_value(&self.rdr) + .ok() + .map(|v| v.into_owned()), _ => None, - } + }, _ => None, } } @@ -303,7 +346,7 @@ impl Reader { // Handle the empty case if !self.parent_has_child() { self.parents.pop(); - return self.next().await + return self.next().await; } // Handle the start/end case @@ -311,11 +354,10 @@ impl Reader { match self.peek() { Event::End(_) => { self.parents.pop(); - return self.next().await - }, + return self.next().await; + } _ => self.skip().await?, }; } } } - -- cgit v1.2.3