From 979a863b2dfccd65f773166066815767a0f75a32 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Sun, 21 Sep 2025 20:37:24 +0200 Subject: [PATCH] some calendar query refactoring --- .../elements.rs} | 142 ++---------------- .../methods/report/calendar_query/mod.rs | 120 +++++++++++++++ 2 files changed, 134 insertions(+), 128 deletions(-) rename crates/caldav/src/calendar/methods/report/{calendar_query.rs => calendar_query/elements.rs} (55%) create mode 100644 crates/caldav/src/calendar/methods/report/calendar_query/mod.rs diff --git a/crates/caldav/src/calendar/methods/report/calendar_query.rs b/crates/caldav/src/calendar/methods/report/calendar_query/elements.rs similarity index 55% rename from crates/caldav/src/calendar/methods/report/calendar_query.rs rename to crates/caldav/src/calendar/methods/report/calendar_query/elements.rs index 5606c62..edfa64e 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/elements.rs @@ -1,7 +1,7 @@ -use crate::{Error, calendar_object::CalendarObjectPropWrapperName}; +use crate::calendar_object::CalendarObjectPropWrapperName; use rustical_dav::xml::PropfindType; use rustical_ical::{CalendarObject, UtcDateTime}; -use rustical_store::{CalendarStore, calendar_store::CalendarQuery}; +use rustical_store::calendar_store::CalendarQuery; use rustical_xml::XmlDeserialize; use std::ops::Deref; @@ -17,24 +17,24 @@ pub(crate) struct TimeRangeElement { #[derive(XmlDeserialize, Clone, Debug, PartialEq)] #[allow(dead_code)] // https://www.rfc-editor.org/rfc/rfc4791#section-9.7.3 -struct ParamFilterElement { +pub struct ParamFilterElement { #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] - is_not_defined: Option<()>, + pub(crate) is_not_defined: Option<()>, #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] - text_match: Option, + pub(crate) text_match: Option, #[xml(ty = "attr")] - name: String, + pub(crate) name: String, } #[derive(XmlDeserialize, Clone, Debug, PartialEq)] #[allow(dead_code)] -struct TextMatchElement { +pub struct TextMatchElement { #[xml(ty = "attr")] - collation: String, + pub(crate) collation: String, #[xml(ty = "attr")] // "yes" or "no", default: "no" - negate_condition: Option, + pub(crate) negate_condition: Option, } #[derive(XmlDeserialize, Clone, Debug, PartialEq)] @@ -42,16 +42,16 @@ struct TextMatchElement { // https://www.rfc-editor.org/rfc/rfc4791#section-9.7.2 pub(crate) struct PropFilterElement { #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] - is_not_defined: Option<()>, + pub(crate) is_not_defined: Option<()>, #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] - time_range: Option, + pub(crate) time_range: Option, #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] - text_match: Option, + pub(crate) text_match: Option, #[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)] - param_filter: Vec, + pub(crate) param_filter: Vec, #[xml(ty = "attr")] - name: String, + pub(crate) name: String, } #[derive(XmlDeserialize, Clone, Debug, PartialEq)] @@ -192,117 +192,3 @@ impl From<&CalendarQueryRequest> for CalendarQuery { .unwrap_or_default() } } - -pub async fn get_objects_calendar_query( - cal_query: &CalendarQueryRequest, - principal: &str, - cal_id: &str, - store: &C, -) -> Result, Error> { - let mut objects = store - .calendar_query(principal, cal_id, cal_query.into()) - .await?; - if let Some(filter) = &cal_query.filter { - objects.retain(|object| filter.matches(object)); - } - Ok(objects) -} - -#[cfg(test)] -mod tests { - use rustical_dav::xml::PropElement; - use rustical_xml::XmlDocument; - - use crate::{ - calendar::methods::report::{ - ReportRequest, - calendar_query::{ - CalendarQueryRequest, CompFilterElement, FilterElement, ParamFilterElement, - PropFilterElement, TextMatchElement, - }, - }, - calendar_object::{CalendarObjectPropName, CalendarObjectPropWrapperName}, - }; - - #[test] - fn calendar_query_7_8_7() { - const INPUT: &str = r#" - - - - - - - - - - - mailto:lisa@example.com - - NEEDS-ACTION - - - - - - - "#; - - let report = ReportRequest::parse_str(INPUT).unwrap(); - let calendar_query: CalendarQueryRequest = - if let ReportRequest::CalendarQuery(query) = report { - query - } else { - panic!() - }; - assert_eq!( - calendar_query, - CalendarQueryRequest { - prop: rustical_dav::xml::PropfindType::Prop(PropElement( - vec![ - CalendarObjectPropWrapperName::CalendarObject( - CalendarObjectPropName::Getetag, - ), - CalendarObjectPropWrapperName::CalendarObject( - CalendarObjectPropName::CalendarData(Default::default()) - ), - ], - vec![] - )), - filter: Some(FilterElement { - comp_filter: CompFilterElement { - is_not_defined: None, - time_range: None, - prop_filter: vec![], - comp_filter: vec![CompFilterElement { - prop_filter: vec![PropFilterElement { - name: "ATTENDEE".to_owned(), - text_match: Some(TextMatchElement { - collation: "i;ascii-casemap".to_owned(), - negate_condition: None - }), - is_not_defined: None, - param_filter: vec![ParamFilterElement { - is_not_defined: None, - name: "PARTSTAT".to_owned(), - text_match: Some(TextMatchElement { - collation: "i;ascii-casemap".to_owned(), - negate_condition: None - }), - }], - time_range: None - }], - comp_filter: vec![], - is_not_defined: None, - name: "VEVENT".to_owned(), - time_range: None - }], - name: "VCALENDAR".to_owned() - } - }), - timezone: None, - timezone_id: None - } - ) - } -} diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs b/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs new file mode 100644 index 0000000..302f33d --- /dev/null +++ b/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs @@ -0,0 +1,120 @@ +use crate::Error; +use rustical_ical::CalendarObject; +use rustical_store::CalendarStore; + +mod elements; +pub(crate) use elements::*; + +pub async fn get_objects_calendar_query( + cal_query: &CalendarQueryRequest, + principal: &str, + cal_id: &str, + store: &C, +) -> Result, Error> { + let mut objects = store + .calendar_query(principal, cal_id, cal_query.into()) + .await?; + if let Some(filter) = &cal_query.filter { + objects.retain(|object| filter.matches(object)); + } + Ok(objects) +} + +#[cfg(test)] +mod tests { + use rustical_dav::xml::PropElement; + use rustical_xml::XmlDocument; + + use crate::{ + calendar::methods::report::{ + ReportRequest, + calendar_query::{ + CalendarQueryRequest, CompFilterElement, FilterElement, ParamFilterElement, + PropFilterElement, TextMatchElement, + }, + }, + calendar_object::{CalendarObjectPropName, CalendarObjectPropWrapperName}, + }; + + #[test] + fn calendar_query_7_8_7() { + const INPUT: &str = r#" + + + + + + + + + + + mailto:lisa@example.com + + NEEDS-ACTION + + + + + + + "#; + + let report = ReportRequest::parse_str(INPUT).unwrap(); + let calendar_query: CalendarQueryRequest = + if let ReportRequest::CalendarQuery(query) = report { + query + } else { + panic!() + }; + assert_eq!( + calendar_query, + CalendarQueryRequest { + prop: rustical_dav::xml::PropfindType::Prop(PropElement( + vec![ + CalendarObjectPropWrapperName::CalendarObject( + CalendarObjectPropName::Getetag, + ), + CalendarObjectPropWrapperName::CalendarObject( + CalendarObjectPropName::CalendarData(Default::default()) + ), + ], + vec![] + )), + filter: Some(FilterElement { + comp_filter: CompFilterElement { + is_not_defined: None, + time_range: None, + prop_filter: vec![], + comp_filter: vec![CompFilterElement { + prop_filter: vec![PropFilterElement { + name: "ATTENDEE".to_owned(), + text_match: Some(TextMatchElement { + collation: "i;ascii-casemap".to_owned(), + negate_condition: None + }), + is_not_defined: None, + param_filter: vec![ParamFilterElement { + is_not_defined: None, + name: "PARTSTAT".to_owned(), + text_match: Some(TextMatchElement { + collation: "i;ascii-casemap".to_owned(), + negate_condition: None + }), + }], + time_range: None + }], + comp_filter: vec![], + is_not_defined: None, + name: "VEVENT".to_owned(), + time_range: None + }], + name: "VCALENDAR".to_owned() + } + }), + timezone: None, + timezone_id: None + } + ) + } +}