mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 21:42:34 +00:00
some calendar query refactoring
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
use crate::{Error, calendar_object::CalendarObjectPropWrapperName};
|
use crate::calendar_object::CalendarObjectPropWrapperName;
|
||||||
use rustical_dav::xml::PropfindType;
|
use rustical_dav::xml::PropfindType;
|
||||||
use rustical_ical::{CalendarObject, UtcDateTime};
|
use rustical_ical::{CalendarObject, UtcDateTime};
|
||||||
use rustical_store::{CalendarStore, calendar_store::CalendarQuery};
|
use rustical_store::calendar_store::CalendarQuery;
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::XmlDeserialize;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
@@ -17,24 +17,24 @@ pub(crate) struct TimeRangeElement {
|
|||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
// https://www.rfc-editor.org/rfc/rfc4791#section-9.7.3
|
// https://www.rfc-editor.org/rfc/rfc4791#section-9.7.3
|
||||||
struct ParamFilterElement {
|
pub struct ParamFilterElement {
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
is_not_defined: Option<()>,
|
pub(crate) is_not_defined: Option<()>,
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
text_match: Option<TextMatchElement>,
|
pub(crate) text_match: Option<TextMatchElement>,
|
||||||
|
|
||||||
#[xml(ty = "attr")]
|
#[xml(ty = "attr")]
|
||||||
name: String,
|
pub(crate) name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
struct TextMatchElement {
|
pub struct TextMatchElement {
|
||||||
#[xml(ty = "attr")]
|
#[xml(ty = "attr")]
|
||||||
collation: String,
|
pub(crate) collation: String,
|
||||||
#[xml(ty = "attr")]
|
#[xml(ty = "attr")]
|
||||||
// "yes" or "no", default: "no"
|
// "yes" or "no", default: "no"
|
||||||
negate_condition: Option<String>,
|
pub(crate) negate_condition: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||||
@@ -42,16 +42,16 @@ struct TextMatchElement {
|
|||||||
// https://www.rfc-editor.org/rfc/rfc4791#section-9.7.2
|
// https://www.rfc-editor.org/rfc/rfc4791#section-9.7.2
|
||||||
pub(crate) struct PropFilterElement {
|
pub(crate) struct PropFilterElement {
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
is_not_defined: Option<()>,
|
pub(crate) is_not_defined: Option<()>,
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
time_range: Option<TimeRangeElement>,
|
pub(crate) time_range: Option<TimeRangeElement>,
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
text_match: Option<TextMatchElement>,
|
pub(crate) text_match: Option<TextMatchElement>,
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)]
|
||||||
param_filter: Vec<ParamFilterElement>,
|
pub(crate) param_filter: Vec<ParamFilterElement>,
|
||||||
|
|
||||||
#[xml(ty = "attr")]
|
#[xml(ty = "attr")]
|
||||||
name: String,
|
pub(crate) name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||||
@@ -192,117 +192,3 @@ impl From<&CalendarQueryRequest> for CalendarQuery {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_objects_calendar_query<C: CalendarStore>(
|
|
||||||
cal_query: &CalendarQueryRequest,
|
|
||||||
principal: &str,
|
|
||||||
cal_id: &str,
|
|
||||||
store: &C,
|
|
||||||
) -> Result<Vec<CalendarObject>, 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#"
|
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
|
|
||||||
<D:prop xmlns:D="DAV:">
|
|
||||||
<D:getetag/>
|
|
||||||
<C:calendar-data/>
|
|
||||||
</D:prop>
|
|
||||||
<C:filter>
|
|
||||||
<C:comp-filter name="VCALENDAR">
|
|
||||||
<C:comp-filter name="VEVENT">
|
|
||||||
<C:prop-filter name="ATTENDEE">
|
|
||||||
<C:text-match collation="i;ascii-casemap">mailto:lisa@example.com</C:text-match>
|
|
||||||
<C:param-filter name="PARTSTAT">
|
|
||||||
<C:text-match collation="i;ascii-casemap">NEEDS-ACTION</C:text-match>
|
|
||||||
</C:param-filter>
|
|
||||||
</C:prop-filter>
|
|
||||||
</C:comp-filter>
|
|
||||||
</C:comp-filter>
|
|
||||||
</C:filter>
|
|
||||||
</C:calendar-query>
|
|
||||||
"#;
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
120
crates/caldav/src/calendar/methods/report/calendar_query/mod.rs
Normal file
120
crates/caldav/src/calendar/methods/report/calendar_query/mod.rs
Normal file
@@ -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<C: CalendarStore>(
|
||||||
|
cal_query: &CalendarQueryRequest,
|
||||||
|
principal: &str,
|
||||||
|
cal_id: &str,
|
||||||
|
store: &C,
|
||||||
|
) -> Result<Vec<CalendarObject>, 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#"
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||||
|
<D:prop xmlns:D="DAV:">
|
||||||
|
<D:getetag/>
|
||||||
|
<C:calendar-data/>
|
||||||
|
</D:prop>
|
||||||
|
<C:filter>
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="ATTENDEE">
|
||||||
|
<C:text-match collation="i;ascii-casemap">mailto:lisa@example.com</C:text-match>
|
||||||
|
<C:param-filter name="PARTSTAT">
|
||||||
|
<C:text-match collation="i;ascii-casemap">NEEDS-ACTION</C:text-match>
|
||||||
|
</C:param-filter>
|
||||||
|
</C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:filter>
|
||||||
|
</C:calendar-query>
|
||||||
|
"#;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user