From 1c9d2eab6976993c85eaa3e8eb4f1c433258fd16 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Tue, 28 May 2024 12:38:22 +0200 Subject: parse property for sync + versioning --- Cargo.lock | 1 + aero-dav/Cargo.toml | 1 + aero-dav/src/caldecoder.rs | 22 +++++++++++ aero-dav/src/calencoder.rs | 19 +++++++++ aero-dav/src/caltypes.rs | 7 ++++ aero-dav/src/realization.rs | 73 +++++++++++++++++++++++++++++++---- aero-dav/src/syncdecoder.rs | 68 +++++++++++++++++++++++++++++++- aero-dav/src/syncencoder.rs | 57 ++++++++++++++++++++++++++- aero-dav/src/synctypes.rs | 17 +++++++- aero-dav/src/types.rs | 1 + aero-dav/src/versioningdecoder.rs | 81 +++++++++++++++++++++++++++++++++++++-- aero-dav/src/versioningencoder.rs | 62 ++++++++++++++++++++++++++++++ aero-dav/src/versioningtypes.rs | 20 ++++++++++ 13 files changed, 414 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d22a5fc..0a159ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,7 @@ dependencies = [ "http 1.1.0", "quick-xml", "tokio", + "tracing", ] [[package]] diff --git a/aero-dav/Cargo.toml b/aero-dav/Cargo.toml index 92929b1..c847f68 100644 --- a/aero-dav/Cargo.toml +++ b/aero-dav/Cargo.toml @@ -12,3 +12,4 @@ http.workspace = true chrono.workspace = true tokio.workspace = true futures.workspace = true +tracing.workspace = true diff --git a/aero-dav/src/caldecoder.rs b/aero-dav/src/caldecoder.rs index ff79845..0df867f 100644 --- a/aero-dav/src/caldecoder.rs +++ b/aero-dav/src/caldecoder.rs @@ -104,6 +104,28 @@ impl QRead for FreeBusyQuery { } } +impl QRead for ReportTypeName { + async fn qread(xml: &mut Reader) -> Result { + if xml.maybe_open(DAV_URN, "calendar-query").await?.is_some() { + xml.close().await?; + return Ok(Self::Query); + } + if xml + .maybe_open(DAV_URN, "calendar-multiget") + .await? + .is_some() + { + xml.close().await?; + return Ok(Self::Multiget); + } + if xml.maybe_open(DAV_URN, "free-busy-query").await?.is_some() { + xml.close().await?; + return Ok(Self::FreeBusy); + } + Err(ParsingError::Recoverable) + } +} + // ---- EXTENSIONS --- impl QRead for Violation { async fn qread(xml: &mut Reader) -> Result { diff --git a/aero-dav/src/calencoder.rs b/aero-dav/src/calencoder.rs index 48c93d0..a41747f 100644 --- a/aero-dav/src/calencoder.rs +++ b/aero-dav/src/calencoder.rs @@ -33,6 +33,25 @@ impl QWrite for MkCalendarResponse { } // ----------------------- REPORT METHOD ------------------------------------- +impl QWrite for ReportTypeName { + async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { + match self { + Self::Query => { + let start = xml.create_dav_element("calendar-query"); + xml.q.write_event_async(Event::Empty(start)).await + } + Self::Multiget => { + let start = xml.create_dav_element("calendar-multiget"); + xml.q.write_event_async(Event::Empty(start)).await + } + Self::FreeBusy => { + let start = xml.create_dav_element("free-busy-query"); + xml.q.write_event_async(Event::Empty(start)).await + } + } + } +} + impl QWrite for ReportType { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { match self { diff --git a/aero-dav/src/caltypes.rs b/aero-dav/src/caltypes.rs index a763653..a4f6fef 100644 --- a/aero-dav/src/caltypes.rs +++ b/aero-dav/src/caltypes.rs @@ -50,6 +50,13 @@ pub struct MkCalendar(pub dav::Set); pub struct MkCalendarResponse(pub Vec>); // --- (REPORT PART) --- +#[derive(Debug, PartialEq, Clone)] +pub enum ReportTypeName { + Query, + Multiget, + FreeBusy, +} + #[derive(Debug, PartialEq, Clone)] pub enum ReportType { Query(CalendarQuery), diff --git a/aero-dav/src/realization.rs b/aero-dav/src/realization.rs index 0f3aec4..b37e0f1 100644 --- a/aero-dav/src/realization.rs +++ b/aero-dav/src/realization.rs @@ -3,6 +3,7 @@ use super::caltypes as cal; use super::error; use super::synctypes as sync; use super::types as dav; +use super::versioningtypes as vers; use super::xml; #[derive(Debug, PartialEq, Clone)] @@ -33,6 +34,7 @@ impl dav::Extension for Core { type PropertyRequest = Disabled; type ResourceType = Disabled; type ReportType = Disabled; + type ReportTypeName = Disabled; } // WebDAV with the base Calendar implementation (RFC4791) @@ -44,6 +46,7 @@ impl dav::Extension for Calendar { type PropertyRequest = cal::PropertyRequest; type ResourceType = cal::ResourceType; type ReportType = cal::ReportType; + type ReportTypeName = cal::ReportTypeName; } // ACL @@ -55,6 +58,7 @@ impl dav::Extension for Acl { type PropertyRequest = acl::PropertyRequest; type ResourceType = acl::ResourceType; type ReportType = Disabled; + type ReportTypeName = Disabled; } // All merged @@ -62,27 +66,38 @@ impl dav::Extension for Acl { pub struct All {} impl dav::Extension for All { type Error = cal::Violation; - type Property = Property; + type Property = Property; type PropertyRequest = PropertyRequest; type ResourceType = ResourceType; type ReportType = ReportType; + type ReportTypeName = ReportTypeName; } #[derive(Debug, PartialEq, Clone)] -pub enum Property { +pub enum Property { Cal(cal::Property), Acl(acl::Property), + Sync(sync::Property), + Vers(vers::Property), } -impl xml::QRead for Property { +impl xml::QRead> for Property { async fn qread(xml: &mut xml::Reader) -> Result { match cal::Property::qread(xml).await { Err(error::ParsingError::Recoverable) => (), - otherwise => return otherwise.map(Property::Cal), + otherwise => return otherwise.map(Property::::Cal), } - acl::Property::qread(xml).await.map(Property::Acl) + match acl::Property::qread(xml).await { + Err(error::ParsingError::Recoverable) => (), + otherwise => return otherwise.map(Property::Acl), + } + match sync::Property::qread(xml).await { + Err(error::ParsingError::Recoverable) => (), + otherwise => return otherwise.map(Property::Sync), + } + vers::Property::qread(xml).await.map(Property::Vers) } } -impl xml::QWrite for Property { +impl xml::QWrite for Property { async fn qwrite( &self, xml: &mut xml::Writer, @@ -90,6 +105,8 @@ impl xml::QWrite for Property { match self { Self::Cal(c) => c.qwrite(xml).await, Self::Acl(a) => a.qwrite(xml).await, + Self::Sync(s) => s.qwrite(xml).await, + Self::Vers(v) => v.qwrite(xml).await, } } } @@ -98,6 +115,8 @@ impl xml::QWrite for Property { pub enum PropertyRequest { Cal(cal::PropertyRequest), Acl(acl::PropertyRequest), + Sync(sync::PropertyRequest), + Vers(vers::PropertyRequest), } impl xml::QRead for PropertyRequest { async fn qread(xml: &mut xml::Reader) -> Result { @@ -105,9 +124,17 @@ impl xml::QRead for PropertyRequest { Err(error::ParsingError::Recoverable) => (), otherwise => return otherwise.map(PropertyRequest::Cal), } - acl::PropertyRequest::qread(xml) + match acl::PropertyRequest::qread(xml).await { + Err(error::ParsingError::Recoverable) => (), + otherwise => return otherwise.map(PropertyRequest::Acl), + } + match sync::PropertyRequest::qread(xml).await { + Err(error::ParsingError::Recoverable) => (), + otherwise => return otherwise.map(PropertyRequest::Sync), + } + vers::PropertyRequest::qread(xml) .await - .map(PropertyRequest::Acl) + .map(PropertyRequest::Vers) } } impl xml::QWrite for PropertyRequest { @@ -118,6 +145,8 @@ impl xml::QWrite for PropertyRequest { match self { Self::Cal(c) => c.qwrite(xml).await, Self::Acl(a) => a.qwrite(xml).await, + Self::Sync(s) => s.qwrite(xml).await, + Self::Vers(v) => v.qwrite(xml).await, } } } @@ -175,3 +204,31 @@ impl xml::QWrite for ReportType { } } } + +#[derive(Debug, PartialEq, Clone)] +pub enum ReportTypeName { + Cal(cal::ReportTypeName), + Sync(sync::ReportTypeName), +} +impl xml::QRead for ReportTypeName { + async fn qread(xml: &mut xml::Reader) -> Result { + match cal::ReportTypeName::qread(xml).await { + Err(error::ParsingError::Recoverable) => (), + otherwise => return otherwise.map(ReportTypeName::Cal), + } + sync::ReportTypeName::qread(xml) + .await + .map(ReportTypeName::Sync) + } +} +impl xml::QWrite for ReportTypeName { + async fn qwrite( + &self, + xml: &mut xml::Writer, + ) -> Result<(), quick_xml::Error> { + match self { + Self::Cal(c) => c.qwrite(xml).await, + Self::Sync(s) => s.qwrite(xml).await, + } + } +} diff --git a/aero-dav/src/syncdecoder.rs b/aero-dav/src/syncdecoder.rs index 8e035ab..d81f8d4 100644 --- a/aero-dav/src/syncdecoder.rs +++ b/aero-dav/src/syncdecoder.rs @@ -5,6 +5,34 @@ use super::synctypes::*; use super::types as dav; use super::xml::{IRead, QRead, Reader, DAV_URN}; +impl QRead for PropertyRequest { + async fn qread(xml: &mut Reader) -> Result { + 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::SyncToken) + } +} + +impl QRead for Property { + async fn qread(xml: &mut Reader) -> Result { + 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::SyncToken) + } +} + +impl QRead for ReportTypeName { + async fn qread(xml: &mut Reader) -> Result { + if xml.maybe_open(DAV_URN, "sync-collection").await?.is_some() { + xml.close().await?; + return Ok(Self::SyncCollection); + } + Err(ParsingError::Recoverable) + } +} + impl QRead> for SyncCollection { async fn qread(xml: &mut Reader) -> Result { xml.open(DAV_URN, "sync-collection").await?; @@ -75,7 +103,7 @@ impl QRead for SyncLevel { #[cfg(test)] mod tests { use super::*; - use crate::realization::All; + use crate::realization::{self, All}; use crate::types as dav; use crate::versioningtypes as vers; use crate::xml::Node; @@ -172,4 +200,42 @@ mod tests { assert_eq!(got, expected); } } + + #[tokio::test] + async fn prop_req() { + let expected = dav::PropName::(vec![dav::PropertyRequest::Extension( + realization::PropertyRequest::Sync(PropertyRequest::SyncToken( + SyncTokenRequest::InitialSync, + )), + )]); + let src = r#""#; + let got = deserialize::>(src).await; + assert_eq!(got, expected); + } + + #[tokio::test] + async fn prop_val() { + let expected = dav::PropValue::(vec![ + dav::Property::Extension(realization::Property::Sync(Property::SyncToken(SyncToken( + "http://example.com/ns/sync/1232".into(), + )))), + dav::Property::Extension(realization::Property::Vers( + vers::Property::SupportedReportSet(vec![vers::SupportedReport( + vers::ReportName::Extension(realization::ReportTypeName::Sync( + ReportTypeName::SyncCollection, + )), + )]), + )), + ]); + let src = r#" + http://example.com/ns/sync/1232 + + + + + + "#; + let got = deserialize::>(src).await; + assert_eq!(got, expected); + } } diff --git a/aero-dav/src/syncencoder.rs b/aero-dav/src/syncencoder.rs index 22b288b..59ad6cc 100644 --- a/aero-dav/src/syncencoder.rs +++ b/aero-dav/src/syncencoder.rs @@ -5,6 +5,33 @@ use super::synctypes::*; use super::types::Extension; use super::xml::{IWrite, QWrite, Writer}; +impl QWrite for Property { + async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { + match self { + Self::SyncToken(token) => token.qwrite(xml).await, + } + } +} + +impl QWrite for PropertyRequest { + async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { + match self { + Self::SyncToken(token) => token.qwrite(xml).await, + } + } +} + +impl QWrite for ReportTypeName { + async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { + match self { + Self::SyncCollection => { + let start = xml.create_dav_element("sync-collection"); + xml.q.write_event_async(Event::Empty(start)).await + } + } + } +} + impl QWrite for SyncCollection { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("sync-collection"); @@ -72,7 +99,7 @@ impl QWrite for SyncLevel { #[cfg(test)] mod tests { use super::*; - use crate::realization::All; + use crate::realization::{self, All}; use crate::types as dav; use crate::versioningtypes as vers; use crate::xml::Node; @@ -92,6 +119,7 @@ mod tests { src.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(); + println!("{:?}", got); // deserialize let mut rdr = Reader::new(quick_xml::NsReader::from_reader(got.as_bytes())) @@ -141,4 +169,31 @@ mod tests { }) .await; } + + #[tokio::test] + async fn prop_req() { + serialize_deserialize(&dav::PropName::(vec![ + dav::PropertyRequest::Extension(realization::PropertyRequest::Sync( + PropertyRequest::SyncToken(SyncTokenRequest::InitialSync), + )), + ])) + .await; + } + + #[tokio::test] + async fn prop_val() { + serialize_deserialize(&dav::PropValue::(vec![ + dav::Property::Extension(realization::Property::Sync(Property::SyncToken(SyncToken( + "http://example.com/ns/sync/1232".into(), + )))), + dav::Property::Extension(realization::Property::Vers( + vers::Property::SupportedReportSet(vec![vers::SupportedReport( + vers::ReportName::Extension(realization::ReportTypeName::Sync( + ReportTypeName::SyncCollection, + )), + )]), + )), + ])) + .await; + } } diff --git a/aero-dav/src/synctypes.rs b/aero-dav/src/synctypes.rs index a2f40bd..c127962 100644 --- a/aero-dav/src/synctypes.rs +++ b/aero-dav/src/synctypes.rs @@ -4,8 +4,21 @@ use super::versioningtypes as vers; // RFC 6578 // https://datatracker.ietf.org/doc/html/rfc6578 -//@FIXME add SyncTokenRequest to PropertyRequest -//@FIXME add SyncToken to Property +#[derive(Debug, PartialEq, Clone)] +pub enum PropertyRequest { + SyncToken(SyncTokenRequest), +} + +#[derive(Debug, PartialEq, Clone)] +pub enum Property { + SyncToken(SyncToken), +} + +#[derive(Debug, PartialEq, Clone)] +pub enum ReportTypeName { + SyncCollection, +} + //@FIXME add SyncToken to Multistatus /// Name: sync-collection diff --git a/aero-dav/src/types.rs b/aero-dav/src/types.rs index 6039a26..92c63e0 100644 --- a/aero-dav/src/types.rs +++ b/aero-dav/src/types.rs @@ -12,6 +12,7 @@ pub trait Extension: std::fmt::Debug + PartialEq + Clone { type PropertyRequest: xml::Node; type ResourceType: xml::Node; type ReportType: xml::Node; + type ReportTypeName: xml::Node; } /// 14.1. activelock XML Element diff --git a/aero-dav/src/versioningdecoder.rs b/aero-dav/src/versioningdecoder.rs index 4816cf1..9e58d8c 100644 --- a/aero-dav/src/versioningdecoder.rs +++ b/aero-dav/src/versioningdecoder.rs @@ -3,12 +3,87 @@ use super::types as dav; use super::versioningtypes::*; use super::xml::{IRead, QRead, Reader, DAV_URN}; +// -- extensions --- +impl QRead for PropertyRequest { + async fn qread(xml: &mut Reader) -> Result { + if xml + .maybe_open(DAV_URN, "supported-report-set") + .await? + .is_some() + { + xml.close().await?; + return Ok(Self::SupportedReportSet); + } + return Err(ParsingError::Recoverable); + } +} + +impl QRead> for Property { + async fn qread(xml: &mut Reader) -> Result { + if xml + .maybe_open(DAV_URN, "supported-report-set") + .await? + .is_some() + { + let supported_reports = xml.collect().await?; + xml.close().await?; + return Ok(Property::SupportedReportSet(supported_reports)); + } + Err(ParsingError::Recoverable) + } +} + +impl QRead> for SupportedReport { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "supported-report").await?; + let r = xml.find().await?; + xml.close().await?; + Ok(SupportedReport(r)) + } +} + +impl QRead> for ReportName { + async fn qread(xml: &mut Reader) -> Result { + xml.open(DAV_URN, "report").await?; + + let final_result = if xml.maybe_open(DAV_URN, "version-tree").await?.is_some() { + xml.close().await?; + Ok(ReportName::VersionTree) + } else if xml.maybe_open(DAV_URN, "expand-property").await?.is_some() { + xml.close().await?; + Ok(ReportName::ExpandProperty) + } else { + let x = match xml.maybe_find().await? { + Some(v) => v, + None => return Err(ParsingError::MissingChild), + }; + Ok(ReportName::Extension(x)) + //E::ReportTypeName::qread(xml).await.map(ReportName::Extension) + }; + + xml.close().await?; + final_result + } +} + impl QRead> for Report { async fn qread(xml: &mut Reader) -> Result { - //@FIXME VersionTree not implemented - //@FIXME ExpandTree not implemented + xml.open(DAV_URN, "report").await?; - E::ReportType::qread(xml).await.map(Report::Extension) + let final_result = if xml.maybe_open(DAV_URN, "version-tree").await?.is_some() { + xml.close().await?; + tracing::warn!("version-tree is not implemented, skipping"); + Ok(Report::VersionTree) + } else if xml.maybe_open(DAV_URN, "expand-property").await?.is_some() { + xml.close().await?; + tracing::warn!("expand-property is not implemented, skipping"); + Ok(Report::ExpandProperty) + } else { + E::ReportType::qread(xml).await.map(Report::Extension) + }; + + xml.close().await?; + final_result } } diff --git a/aero-dav/src/versioningencoder.rs b/aero-dav/src/versioningencoder.rs index bd40f1b..c061f07 100644 --- a/aero-dav/src/versioningencoder.rs +++ b/aero-dav/src/versioningencoder.rs @@ -5,6 +5,67 @@ use super::types::Extension; use super::versioningtypes::*; use super::xml::{IWrite, QWrite, Writer}; +// --- extensions to PROP +impl QWrite for PropertyRequest { + async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { + match self { + Self::SupportedReportSet => { + let start = xml.create_dav_element("supported-report-set"); + xml.q.write_event_async(Event::Empty(start)).await + } + } + } +} + +impl QWrite for Property { + async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { + match self { + Self::SupportedReportSet(set) => { + let start = xml.create_dav_element("supported-report-set"); + let end = start.to_end(); + + xml.q.write_event_async(Event::Start(start.clone())).await?; + for v in set.iter() { + v.qwrite(xml).await?; + } + xml.q.write_event_async(Event::End(end)).await + } + } + } +} + +impl QWrite for SupportedReport { + async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { + let start = xml.create_dav_element("supported-report"); + let end = start.to_end(); + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.0.qwrite(xml).await?; + xml.q.write_event_async(Event::End(end)).await + } +} + +impl QWrite for ReportName { + async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { + let start = xml.create_dav_element("report"); + let end = start.to_end(); + + xml.q.write_event_async(Event::Start(start.clone())).await?; + match self { + Self::VersionTree => { + let start = xml.create_dav_element("version-tree"); + xml.q.write_event_async(Event::Empty(start)).await?; + } + Self::ExpandProperty => { + let start = xml.create_dav_element("expand-property"); + xml.q.write_event_async(Event::Empty(start)).await?; + } + Self::Extension(ext) => ext.qwrite(xml).await?, + }; + xml.q.write_event_async(Event::End(end)).await + } +} + +// --- root REPORT object --- impl QWrite for Report { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { match self { @@ -15,6 +76,7 @@ impl QWrite for Report { } } +// --- limit REPORT parameter --- impl QWrite for Limit { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { let start = xml.create_dav_element("limit"); diff --git a/aero-dav/src/versioningtypes.rs b/aero-dav/src/versioningtypes.rs index ba64b05..1f8d1cf 100644 --- a/aero-dav/src/versioningtypes.rs +++ b/aero-dav/src/versioningtypes.rs @@ -21,6 +21,26 @@ use super::types as dav; // // ANY value: a report element type +#[derive(Debug, PartialEq, Clone)] +pub enum PropertyRequest { + SupportedReportSet, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum Property { + SupportedReportSet(Vec>), +} + +#[derive(Debug, PartialEq, Clone)] +pub struct SupportedReport(pub ReportName); + +#[derive(Debug, PartialEq, Clone)] +pub enum ReportName { + VersionTree, + ExpandProperty, + Extension(E::ReportTypeName), +} + #[derive(Debug, PartialEq, Clone)] pub enum Report { VersionTree, // Not yet implemented -- cgit v1.2.3