rustical_caldav/calendar/methods/report/calendar_query/
mod.rs

1use crate::Error;
2use rustical_ical::CalendarObject;
3use rustical_store::CalendarStore;
4
5mod comp_filter;
6mod elements;
7mod prop_filter;
8#[cfg(test)]
9mod tests;
10#[allow(unused_imports)]
11pub use comp_filter::{CompFilterElement, CompFilterable};
12pub use elements::*;
13#[allow(unused_imports)]
14pub use prop_filter::PropFilterElement;
15
16pub async fn get_objects_calendar_query<C: CalendarStore>(
17    cal_query: &CalendarQueryRequest,
18    principal: &str,
19    cal_id: &str,
20    store: &C,
21) -> Result<Vec<(String, CalendarObject)>, Error> {
22    let mut objects = store
23        .calendar_query(principal, cal_id, cal_query.into())
24        .await?;
25    if let Some(filter) = &cal_query.filter {
26        objects.retain(|(_id, object)| filter.matches(object.get_inner()));
27    }
28    Ok(objects)
29}
30
31#[cfg(test)]
32mod xml_tests {
33    use super::{
34        CalendarQueryRequest, FilterElement, ParamFilterElement, comp_filter::CompFilterElement,
35        prop_filter::PropFilterElement,
36    };
37    use crate::{
38        calendar::methods::report::ReportRequest,
39        calendar_object::{CalendarData, CalendarObjectPropName, CalendarObjectPropWrapperName},
40    };
41    use rustical_dav::xml::{
42        MatchType, NegateCondition, PropElement, TextCollation, TextMatchElement,
43    };
44    use rustical_xml::XmlDocument;
45
46    #[test]
47    fn calendar_query_7_8_7() {
48        const INPUT: &str = r#"
49            <?xml version="1.0" encoding="utf-8" ?>
50            <C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
51                <D:prop xmlns:D="DAV:">
52                    <D:getetag/>
53                    <C:calendar-data/>
54                </D:prop>
55                <C:filter>
56                    <C:comp-filter name="VCALENDAR">
57                        <C:comp-filter name="VEVENT">
58                            <C:prop-filter name="ATTENDEE">
59                                <C:text-match collation="i;ascii-casemap">mailto:lisa@example.com</C:text-match>
60                                <C:param-filter name="PARTSTAT">
61                                    <C:text-match collation="i;ascii-casemap">NEEDS-ACTION</C:text-match>
62                                </C:param-filter>
63                            </C:prop-filter>
64                        </C:comp-filter>
65                    </C:comp-filter>
66                </C:filter>
67            </C:calendar-query>
68        "#;
69
70        let report = ReportRequest::parse_str(INPUT).unwrap();
71        let calendar_query: CalendarQueryRequest =
72            if let ReportRequest::CalendarQuery(query) = report {
73                query
74            } else {
75                panic!()
76            };
77        assert_eq!(
78            calendar_query,
79            CalendarQueryRequest {
80                prop: rustical_dav::xml::PropfindType::Prop(PropElement(
81                    vec![
82                        CalendarObjectPropWrapperName::CalendarObject(
83                            CalendarObjectPropName::Getetag,
84                        ),
85                        CalendarObjectPropWrapperName::CalendarObject(
86                            CalendarObjectPropName::CalendarData(CalendarData::default())
87                        ),
88                    ],
89                    vec![]
90                )),
91                filter: Some(FilterElement {
92                    comp_filter: CompFilterElement {
93                        is_not_defined: None,
94                        time_range: None,
95                        prop_filter: vec![],
96                        comp_filter: vec![CompFilterElement {
97                            prop_filter: vec![PropFilterElement {
98                                name: "ATTENDEE".to_owned(),
99                                text_match: Some(TextMatchElement {
100                                    match_type: MatchType::Contains,
101                                    collation: TextCollation::AsciiCasemap,
102                                    negate_condition: NegateCondition(false),
103                                    needle: "mailto:lisa@example.com".to_string()
104                                }),
105                                is_not_defined: None,
106                                param_filter: vec![ParamFilterElement {
107                                    is_not_defined: None,
108                                    name: "PARTSTAT".to_owned(),
109                                    text_match: Some(TextMatchElement {
110                                        match_type: MatchType::Contains,
111                                        collation: TextCollation::AsciiCasemap,
112                                        negate_condition: NegateCondition(false),
113                                        needle: "NEEDS-ACTION".to_string()
114                                    }),
115                                }],
116                                time_range: None
117                            }],
118                            comp_filter: vec![],
119                            is_not_defined: None,
120                            name: "VEVENT".to_owned(),
121                            time_range: None
122                        }],
123                        name: "VCALENDAR".to_owned()
124                    }
125                }),
126                timezone: None,
127                timezone_id: None
128            }
129        );
130    }
131}