rustical_caldav/calendar/methods/report/calendar_query/
elements.rs1use super::comp_filter::{CompFilterElement, CompFilterable};
2use crate::calendar_object::CalendarObjectPropWrapperName;
3use caldata::{component::IcalCalendarObject, parser::ContentLine};
4use rustical_dav::xml::{PropfindType, TextMatchElement};
5use rustical_ical::UtcDateTime;
6use rustical_store::calendar_store::CalendarQuery;
7use rustical_xml::{XmlDeserialize, XmlRootTag};
8
9#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)]
10#[allow(dead_code)]
11pub struct TimeRangeElement {
12 #[xml(ty = "attr")]
13 pub(crate) start: Option<UtcDateTime>,
14 #[xml(ty = "attr")]
15 pub(crate) end: Option<UtcDateTime>,
16}
17
18#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)]
19#[allow(dead_code)]
20pub struct ParamFilterElement {
22 #[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
23 pub(crate) is_not_defined: Option<()>,
24 #[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
25 pub(crate) text_match: Option<TextMatchElement>,
26
27 #[xml(ty = "attr")]
28 pub(crate) name: String,
29}
30
31impl ParamFilterElement {
32 #[must_use]
33 pub fn match_property(&self, prop: &ContentLine) -> bool {
34 let Some(param) = prop.params.get_param(&self.name) else {
35 return self.is_not_defined.is_some();
36 };
37 if self.is_not_defined.is_some() {
38 return false;
39 }
40
41 let Some(text_match) = self.text_match.as_ref() else {
42 return true;
43 };
44 text_match.match_text(param)
45 }
46}
47
48#[derive(XmlDeserialize, XmlRootTag, Clone, Debug, PartialEq)]
49#[xml(root = "filter", ns = "rustical_dav::namespace::NS_CALDAV")]
50#[allow(dead_code)]
51pub struct FilterElement {
53 #[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
55 pub(crate) comp_filter: CompFilterElement,
56}
57
58impl FilterElement {
59 #[must_use]
60 pub fn matches(&self, cal_object: &IcalCalendarObject) -> bool {
61 cal_object.matches(&self.comp_filter)
62 }
63}
64
65impl From<&FilterElement> for CalendarQuery {
66 fn from(value: &FilterElement) -> Self {
67 let comp_filter_vcalendar = &value.comp_filter;
68 for comp_filter in &comp_filter_vcalendar.comp_filter {
69 if matches!(comp_filter.name.as_str(), "VEVENT" | "VTODO")
72 && let Some(time_range) = &comp_filter.time_range
73 {
74 let start = time_range.start.as_ref().map(|start| start.date_naive());
75 let end = time_range.end.as_ref().map(|end| end.date_naive());
76 return Self {
77 time_start: start,
78 time_end: end,
79 };
80 }
81 }
82 Self::default()
83 }
84}
85
86#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
87#[allow(dead_code)]
88pub struct CalendarQueryRequest {
90 #[xml(ty = "untagged")]
91 pub prop: PropfindType<CalendarObjectPropWrapperName>,
92 #[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
93 pub(crate) filter: Option<FilterElement>,
94 #[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
95 pub(crate) timezone: Option<String>,
96 #[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
97 pub(crate) timezone_id: Option<String>,
98}
99
100impl From<&CalendarQueryRequest> for CalendarQuery {
101 fn from(value: &CalendarQueryRequest) -> Self {
102 value.filter.as_ref().map(Self::from).unwrap_or_default()
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use crate::calendar::methods::report::calendar_query::{
109 CompFilterElement, FilterElement, TimeRangeElement,
110 };
111 use chrono::{NaiveDate, TimeZone, Utc};
112 use rustical_ical::UtcDateTime;
113 use rustical_store::calendar_store::CalendarQuery;
114
115 #[test]
116 fn test_filter_element_calendar_query() {
117 let filter = FilterElement {
118 comp_filter: CompFilterElement {
119 name: "VCALENDAR".to_string(),
120 is_not_defined: None,
121 time_range: None,
122 prop_filter: vec![],
123 comp_filter: vec![CompFilterElement {
124 name: "VEVENT".to_string(),
125 is_not_defined: None,
126 time_range: Some(TimeRangeElement {
127 start: Some(UtcDateTime(
128 Utc.with_ymd_and_hms(2024, 4, 1, 0, 0, 0).unwrap(),
129 )),
130 end: Some(UtcDateTime(
131 Utc.with_ymd_and_hms(2024, 8, 1, 0, 0, 0).unwrap(),
132 )),
133 }),
134 prop_filter: vec![],
135 comp_filter: vec![],
136 }],
137 },
138 };
139 let derived_query: CalendarQuery = (&filter).into();
140 let query = CalendarQuery {
141 time_start: Some(NaiveDate::from_ymd_opt(2024, 4, 1).unwrap()),
142 time_end: Some(NaiveDate::from_ymd_opt(2024, 8, 1).unwrap()),
143 };
144 assert_eq!(derived_query, query);
145 }
146}