parse property for sync + versioning

This commit is contained in:
Quentin 2024-05-28 12:38:22 +02:00
parent 5b1da2a33b
commit 1c9d2eab69
Signed by: quentin
GPG key ID: E9602264D639FF68
13 changed files with 414 additions and 15 deletions

1
Cargo.lock generated
View file

@ -70,6 +70,7 @@ dependencies = [
"http 1.1.0",
"quick-xml",
"tokio",
"tracing",
]
[[package]]

View file

@ -12,3 +12,4 @@ http.workspace = true
chrono.workspace = true
tokio.workspace = true
futures.workspace = true
tracing.workspace = true

View file

@ -104,6 +104,28 @@ impl QRead<FreeBusyQuery> for FreeBusyQuery {
}
}
impl QRead<ReportTypeName> for ReportTypeName {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
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<Violation> for Violation {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {

View file

@ -33,6 +33,25 @@ impl<E: Extension> QWrite for MkCalendarResponse<E> {
}
// ----------------------- REPORT METHOD -------------------------------------
impl QWrite for ReportTypeName {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> 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<E: Extension> QWrite for ReportType<E> {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
match self {

View file

@ -50,6 +50,13 @@ pub struct MkCalendar<E: dav::Extension>(pub dav::Set<E>);
pub struct MkCalendarResponse<E: dav::Extension>(pub Vec<dav::PropStat<E>>);
// --- (REPORT PART) ---
#[derive(Debug, PartialEq, Clone)]
pub enum ReportTypeName {
Query,
Multiget,
FreeBusy,
}
#[derive(Debug, PartialEq, Clone)]
pub enum ReportType<E: dav::Extension> {
Query(CalendarQuery<E>),

View file

@ -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<Calendar>;
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<All>;
type PropertyRequest = PropertyRequest;
type ResourceType = ResourceType;
type ReportType = ReportType<All>;
type ReportTypeName = ReportTypeName;
}
#[derive(Debug, PartialEq, Clone)]
pub enum Property {
pub enum Property<E: dav::Extension> {
Cal(cal::Property),
Acl(acl::Property),
Sync(sync::Property),
Vers(vers::Property<E>),
}
impl xml::QRead<Property> for Property {
impl<E: dav::Extension> xml::QRead<Property<E>> for Property<E> {
async fn qread(xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
match cal::Property::qread(xml).await {
Err(error::ParsingError::Recoverable) => (),
otherwise => return otherwise.map(Property::Cal),
otherwise => return otherwise.map(Property::<E>::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<E: dav::Extension> xml::QWrite for Property<E> {
async fn qwrite(
&self,
xml: &mut xml::Writer<impl xml::IWrite>,
@ -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<PropertyRequest> for PropertyRequest {
async fn qread(xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
@ -105,9 +124,17 @@ impl xml::QRead<PropertyRequest> 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<E: dav::Extension> xml::QWrite for ReportType<E> {
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum ReportTypeName {
Cal(cal::ReportTypeName),
Sync(sync::ReportTypeName),
}
impl xml::QRead<ReportTypeName> for ReportTypeName {
async fn qread(xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
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<impl xml::IWrite>,
) -> Result<(), quick_xml::Error> {
match self {
Self::Cal(c) => c.qwrite(xml).await,
Self::Sync(s) => s.qwrite(xml).await,
}
}
}

View file

@ -5,6 +5,34 @@ use super::synctypes::*;
use super::types as dav;
use super::xml::{IRead, QRead, Reader, DAV_URN};
impl QRead<PropertyRequest> for PropertyRequest {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
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<Property> for Property {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
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<ReportTypeName> for ReportTypeName {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
if xml.maybe_open(DAV_URN, "sync-collection").await?.is_some() {
xml.close().await?;
return Ok(Self::SyncCollection);
}
Err(ParsingError::Recoverable)
}
}
impl<E: dav::Extension> QRead<SyncCollection<E>> for SyncCollection<E> {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
xml.open(DAV_URN, "sync-collection").await?;
@ -75,7 +103,7 @@ impl QRead<SyncLevel> 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::<All>(vec![dav::PropertyRequest::Extension(
realization::PropertyRequest::Sync(PropertyRequest::SyncToken(
SyncTokenRequest::InitialSync,
)),
)]);
let src = r#"<prop xmlns="DAV:"><sync-token/></prop>"#;
let got = deserialize::<dav::PropName<All>>(src).await;
assert_eq!(got, expected);
}
#[tokio::test]
async fn prop_val() {
let expected = dav::PropValue::<All>(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#"<prop xmlns="DAV:">
<sync-token>http://example.com/ns/sync/1232</sync-token>
<supported-report-set>
<supported-report>
<report><sync-collection/></report>
</supported-report>
</supported-report-set>
</prop>"#;
let got = deserialize::<dav::PropValue<All>>(src).await;
assert_eq!(got, expected);
}
}

View file

@ -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<impl IWrite>) -> Result<(), QError> {
match self {
Self::SyncToken(token) => token.qwrite(xml).await,
}
}
}
impl QWrite for PropertyRequest {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
match self {
Self::SyncToken(token) => token.qwrite(xml).await,
}
}
}
impl QWrite for ReportTypeName {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
match self {
Self::SyncCollection => {
let start = xml.create_dav_element("sync-collection");
xml.q.write_event_async(Event::Empty(start)).await
}
}
}
}
impl<E: Extension> QWrite for SyncCollection<E> {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> 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::<All>(vec![
dav::PropertyRequest::Extension(realization::PropertyRequest::Sync(
PropertyRequest::SyncToken(SyncTokenRequest::InitialSync),
)),
]))
.await;
}
#[tokio::test]
async fn prop_val() {
serialize_deserialize(&dav::PropValue::<All>(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;
}
}

View file

@ -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

View file

@ -12,6 +12,7 @@ pub trait Extension: std::fmt::Debug + PartialEq + Clone {
type PropertyRequest: xml::Node<Self::PropertyRequest>;
type ResourceType: xml::Node<Self::ResourceType>;
type ReportType: xml::Node<Self::ReportType>;
type ReportTypeName: xml::Node<Self::ReportTypeName>;
}
/// 14.1. activelock XML Element

View file

@ -3,12 +3,87 @@ use super::types as dav;
use super::versioningtypes::*;
use super::xml::{IRead, QRead, Reader, DAV_URN};
// -- extensions ---
impl QRead<PropertyRequest> for PropertyRequest {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
if xml
.maybe_open(DAV_URN, "supported-report-set")
.await?
.is_some()
{
xml.close().await?;
return Ok(Self::SupportedReportSet);
}
return Err(ParsingError::Recoverable);
}
}
impl<E: dav::Extension> QRead<Property<E>> for Property<E> {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
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<E: dav::Extension> QRead<SupportedReport<E>> for SupportedReport<E> {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
xml.open(DAV_URN, "supported-report").await?;
let r = xml.find().await?;
xml.close().await?;
Ok(SupportedReport(r))
}
}
impl<E: dav::Extension> QRead<ReportName<E>> for ReportName<E> {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
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<E: dav::Extension> QRead<Report<E>> for Report<E> {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
//@FIXME VersionTree not implemented
//@FIXME ExpandTree not implemented
xml.open(DAV_URN, "report").await?;
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
}
}

View file

@ -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<impl IWrite>) -> 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<E: Extension> QWrite for Property<E> {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> 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<E: Extension> QWrite for SupportedReport<E> {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> 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<E: Extension> QWrite for ReportName<E> {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> 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<E: Extension> QWrite for Report<E> {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
match self {
@ -15,6 +76,7 @@ impl<E: Extension> QWrite for Report<E> {
}
}
// --- limit REPORT parameter ---
impl QWrite for Limit {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
let start = xml.create_dav_element("limit");

View file

@ -21,6 +21,26 @@ use super::types as dav;
// <!ELEMENT report ANY>
// ANY value: a report element type
#[derive(Debug, PartialEq, Clone)]
pub enum PropertyRequest {
SupportedReportSet,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Property<E: dav::Extension> {
SupportedReportSet(Vec<SupportedReport<E>>),
}
#[derive(Debug, PartialEq, Clone)]
pub struct SupportedReport<E: dav::Extension>(pub ReportName<E>);
#[derive(Debug, PartialEq, Clone)]
pub enum ReportName<E: dav::Extension> {
VersionTree,
ExpandProperty,
Extension(E::ReportTypeName),
}
#[derive(Debug, PartialEq, Clone)]
pub enum Report<E: dav::Extension> {
VersionTree, // Not yet implemented