diff --git a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs index dad0c08..166ec21 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs @@ -1,17 +1,18 @@ +use super::ReportPropName; use crate::{ - calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource}, Error, + calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource}, }; use actix_web::{ + HttpRequest, dev::{Path, ResourceDef}, http::StatusCode, - HttpRequest, }; use rustical_dav::{ resource::Resource, - xml::{multistatus::ResponseElement, MultistatusElement, PropElement, PropfindType}, + xml::{MultistatusElement, PropElement, PropfindType, multistatus::ResponseElement}, }; -use rustical_store::{auth::User, CalendarObject, CalendarStore}; +use rustical_store::{CalendarObject, CalendarStore, auth::User}; use rustical_xml::XmlDeserialize; #[derive(XmlDeserialize, Clone, Debug, PartialEq)] @@ -19,7 +20,7 @@ use rustical_xml::XmlDeserialize; // pub(crate) struct CalendarMultigetRequest { #[xml(ty = "untagged")] - pub(crate) prop: PropfindType, + pub(crate) prop: PropfindType, #[xml(flatten)] #[xml(ns = "rustical_dav::namespace::NS_DAV")] pub(crate) href: Vec, @@ -72,9 +73,16 @@ pub async fn handle_calendar_multiget( PropfindType::Propname => { vec!["propname".to_owned()] } - PropfindType::Prop(PropElement(prop_tags)) => { - prop_tags.into_iter().map(|propname| propname.0).collect() - } + PropfindType::Prop(PropElement(prop_tags)) => prop_tags + .into_iter() + .filter_map(|propname| { + if let ReportPropName::Propname(propname) = propname { + Some(propname.0) + } else { + None + } + }) + .collect(), }; let props: Vec<&str> = props.iter().map(String::as_str).collect(); diff --git a/crates/caldav/src/calendar/methods/report/calendar_query.rs b/crates/caldav/src/calendar/methods/report/calendar_query.rs index 673dafb..bda8471 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query.rs @@ -4,16 +4,18 @@ use rustical_dav::{ xml::{MultistatusElement, PropElement, PropfindType}, }; use rustical_store::{ - auth::User, calendar::UtcDateTime, calendar_store::CalendarQuery, CalendarObject, CalendarStore, + CalendarObject, CalendarStore, auth::User, calendar::UtcDateTime, calendar_store::CalendarQuery, }; use rustical_xml::XmlDeserialize; use std::ops::Deref; use crate::{ - calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource}, Error, + calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource}, }; +use super::ReportPropName; + #[derive(XmlDeserialize, Clone, Debug, PartialEq)] #[allow(dead_code)] pub(crate) struct TimeRangeElement { @@ -179,7 +181,7 @@ impl From<&FilterElement> for CalendarQuery { // pub struct CalendarQueryRequest { #[xml(ty = "untagged")] - pub prop: PropfindType, + pub prop: PropfindType, #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] pub(crate) filter: Option, #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] @@ -230,9 +232,16 @@ pub async fn handle_calendar_query( PropfindType::Propname => { vec!["propname".to_owned()] } - PropfindType::Prop(PropElement(prop_tags)) => { - prop_tags.into_iter().map(|propname| propname.0).collect() - } + PropfindType::Prop(PropElement(prop_tags)) => prop_tags + .into_iter() + .filter_map(|propname| { + if let ReportPropName::Propname(propname) = propname { + Some(propname.0) + } else { + None + } + }) + .collect(), }; let props: Vec<&str> = props.iter().map(String::as_str).collect(); diff --git a/crates/caldav/src/calendar/methods/report/mod.rs b/crates/caldav/src/calendar/methods/report/mod.rs index e7007d1..9aa7c99 100644 --- a/crates/caldav/src/calendar/methods/report/mod.rs +++ b/crates/caldav/src/calendar/methods/report/mod.rs @@ -1,12 +1,12 @@ use crate::Error; use actix_web::{ - web::{Data, Path}, HttpRequest, Responder, + web::{Data, Path}, }; -use calendar_multiget::{handle_calendar_multiget, CalendarMultigetRequest}; -use calendar_query::{handle_calendar_query, CalendarQueryRequest}; -use rustical_dav::xml::sync_collection::SyncCollectionRequest; -use rustical_store::{auth::User, CalendarStore}; +use calendar_multiget::{CalendarMultigetRequest, handle_calendar_multiget}; +use calendar_query::{CalendarQueryRequest, handle_calendar_query}; +use rustical_dav::xml::{Propname, sync_collection::SyncCollectionRequest}; +use rustical_store::{CalendarStore, auth::User}; use rustical_xml::{XmlDeserialize, XmlDocument}; use sync_collection::handle_sync_collection; use tracing::instrument; @@ -15,6 +15,34 @@ mod calendar_multiget; mod calendar_query; mod sync_collection; +#[derive(XmlDeserialize, Clone, Debug, PartialEq)] +pub(crate) struct ExpandElement { + #[xml(ty = "attr")] + start: String, + #[xml(ty = "attr")] + end: String, +} + +#[derive(XmlDeserialize, Clone, Debug, PartialEq)] +pub struct CalendarData { + #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] + comp: Option<()>, + #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] + expand: Option, + #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] + limit_recurrence_set: Option<()>, + #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] + limit_freebusy_set: Option<()>, +} + +#[derive(XmlDeserialize, Clone, Debug, PartialEq)] +pub enum ReportPropName { + #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] + CalendarData(CalendarData), + #[xml(other)] + Propname(Propname), +} + #[derive(XmlDeserialize, XmlDocument, Clone, Debug, PartialEq)] pub(crate) enum ReportRequest { #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] @@ -22,7 +50,7 @@ pub(crate) enum ReportRequest { #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] CalendarQuery(CalendarQueryRequest), #[xml(ns = "rustical_dav::namespace::NS_DAV")] - SyncCollection(SyncCollectionRequest), + SyncCollection(SyncCollectionRequest), } #[instrument(skip(req, cal_store))] @@ -79,12 +107,42 @@ pub async fn route_report_calendar( #[cfg(test)] mod tests { + use super::*; use calendar_query::{CompFilterElement, FilterElement, TimeRangeElement}; use rustical_dav::xml::{PropElement, PropfindType, Propname}; use rustical_store::calendar::UtcDateTime; use rustical_xml::ValueDeserialize; - use super::*; + #[test] + fn test_xml_calendar_data() { + let report_request = ReportRequest::parse_str(r#" + + + + + + + + + + /caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b + + "#).unwrap(); + + assert_eq!( + report_request, + ReportRequest::CalendarMultiget(CalendarMultigetRequest { + prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![ + ReportPropName::Propname(Propname("getetag".to_owned())), + ReportPropName::Propname(Propname("displayname".to_owned())), + ReportPropName::CalendarData(CalendarData { comp: None, expand: Some(ExpandElement { start: "20250426T220000Z".to_owned(), end: "20250503T220000Z".to_owned() }), limit_recurrence_set: None, limit_freebusy_set: None }) + ])), + href: vec![ + "/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned() + ] + }) + ) + } #[test] fn test_xml_calendar_query() { @@ -108,7 +166,9 @@ mod tests { assert_eq!( report_request, ReportRequest::CalendarQuery(CalendarQueryRequest { - prop: PropfindType::Prop(PropElement(vec![Propname("getetag".to_owned())])), + prop: PropfindType::Prop(PropElement(vec![ReportPropName::Propname(Propname( + "getetag".to_owned() + ))])), filter: Some(FilterElement { comp_filter: CompFilterElement { is_not_defined: None, @@ -155,8 +215,8 @@ mod tests { report_request, ReportRequest::CalendarMultiget(CalendarMultigetRequest { prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![ - Propname("getetag".to_owned()), - Propname("displayname".to_owned()) + ReportPropName::Propname(Propname("getetag".to_owned())), + ReportPropName::Propname(Propname("displayname".to_owned())) ])), href: vec![ "/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned() diff --git a/crates/caldav/src/calendar/methods/report/sync_collection.rs b/crates/caldav/src/calendar/methods/report/sync_collection.rs index 763024e..3ffc03a 100644 --- a/crates/caldav/src/calendar/methods/report/sync_collection.rs +++ b/crates/caldav/src/calendar/methods/report/sync_collection.rs @@ -1,24 +1,24 @@ -use actix_web::{http::StatusCode, HttpRequest}; +use super::ReportPropName; +use crate::{ + Error, + calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource}, +}; +use actix_web::{HttpRequest, http::StatusCode}; use rustical_dav::{ resource::Resource, xml::{ - multistatus::ResponseElement, sync_collection::SyncCollectionRequest, MultistatusElement, - PropElement, PropfindType, + MultistatusElement, PropElement, PropfindType, multistatus::ResponseElement, + sync_collection::SyncCollectionRequest, }, }; use rustical_store::{ + CalendarStore, auth::User, synctoken::{format_synctoken, parse_synctoken}, - CalendarStore, -}; - -use crate::{ - calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource}, - Error, }; pub async fn handle_sync_collection( - sync_collection: SyncCollectionRequest, + sync_collection: SyncCollectionRequest, req: HttpRequest, user: &User, principal: &str, @@ -32,9 +32,16 @@ pub async fn handle_sync_collection( PropfindType::Propname => { vec!["propname".to_owned()] } - PropfindType::Prop(PropElement(prop_tags)) => { - prop_tags.into_iter().map(|propname| propname.0).collect() - } + PropfindType::Prop(PropElement(prop_tags)) => prop_tags + .into_iter() + .filter_map(|propname| { + if let ReportPropName::Propname(propname) = propname { + Some(propname.0) + } else { + None + } + }) + .collect(), }; let props: Vec<&str> = props.iter().map(String::as_str).collect(); diff --git a/crates/dav/src/xml/propfind.rs b/crates/dav/src/xml/propfind.rs index a8815e5..3c23f3c 100644 --- a/crates/dav/src/xml/propfind.rs +++ b/crates/dav/src/xml/propfind.rs @@ -9,17 +9,17 @@ pub struct PropfindElement { } #[derive(Debug, Clone, XmlDeserialize, PartialEq)] -pub struct PropElement(#[xml(ty = "untagged", flatten)] pub Vec); +pub struct PropElement(#[xml(ty = "untagged", flatten)] pub Vec); #[derive(Debug, Clone, XmlDeserialize, PartialEq)] pub struct Propname(#[xml(ty = "tag_name")] pub String); #[derive(Debug, Clone, XmlDeserialize, PartialEq)] -pub enum PropfindType { +pub enum PropfindType { #[xml(ns = "crate::namespace::NS_DAV")] Propname, #[xml(ns = "crate::namespace::NS_DAV")] Allprop, #[xml(ns = "crate::namespace::NS_DAV")] - Prop(PropElement), + Prop(PropElement), } diff --git a/crates/dav/src/xml/sync_collection.rs b/crates/dav/src/xml/sync_collection.rs index deb53b4..cdcf81f 100644 --- a/crates/dav/src/xml/sync_collection.rs +++ b/crates/dav/src/xml/sync_collection.rs @@ -1,6 +1,6 @@ use rustical_xml::{ValueDeserialize, ValueSerialize, XmlDeserialize}; -use super::PropfindType; +use super::{PropfindType, Propname}; #[derive(Clone, Debug, PartialEq)] pub enum SyncLevel { @@ -16,7 +16,7 @@ impl ValueDeserialize for SyncLevel { _ => { return Err(rustical_xml::XmlError::InvalidValue( rustical_xml::ParseValueError::Other("Invalid sync-level".to_owned()), - )) + )); } }) } @@ -37,13 +37,13 @@ impl ValueSerialize for SyncLevel { // // #[xml(ns = "crate::namespace::NS_DAV")] -pub struct SyncCollectionRequest { +pub struct SyncCollectionRequest { #[xml(ns = "crate::namespace::NS_DAV")] pub sync_token: String, #[xml(ns = "crate::namespace::NS_DAV")] pub sync_level: SyncLevel, #[xml(ns = "crate::namespace::NS_DAV", ty = "untagged")] - pub prop: PropfindType, + pub prop: PropfindType, #[xml(ns = "crate::namespace::NS_DAV")] pub limit: Option, }