aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--aero-dav/src/caldecoder.rs272
-rw-r--r--aero-dav/src/xml.rs8
2 files changed, 253 insertions, 27 deletions
diff --git a/aero-dav/src/caldecoder.rs b/aero-dav/src/caldecoder.rs
index 2e833a1..d3c68f6 100644
--- a/aero-dav/src/caldecoder.rs
+++ b/aero-dav/src/caldecoder.rs
@@ -323,9 +323,13 @@ impl QRead<CalendarDataRequest> for CalendarDataRequest {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
xml.open(CAL_URN, "calendar-data").await?;
let mime = CalendarDataSupport::qread(xml).await.ok();
-
let (mut comp, mut recurrence, mut limit_freebusy_set) = (None, None, None);
+ if !xml.parent_has_child() {
+ return Ok(Self { mime, comp, recurrence, limit_freebusy_set })
+ }
+
+
loop {
let mut dirty = false;
xml.maybe_read(&mut comp, &mut dirty).await?;
@@ -566,22 +570,18 @@ impl QRead<CompFilter> for CompFilter {
impl QRead<CompFilterRules> for CompFilterRules {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- if xml.maybe_open(CAL_URN, "is-not-defined").await?.is_some() {
- xml.close().await?;
- return Ok(Self::IsNotDefined)
- }
- CompFilterMatch::qread(xml).await.map(CompFilterRules::Matches)
- }
-}
-
-impl QRead<CompFilterMatch> for CompFilterMatch {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
let mut time_range = None;
let mut prop_filter = Vec::new();
let mut comp_filter = Vec::new();
loop {
let mut dirty = false;
+
+ if xml.maybe_open(CAL_URN, "is-not-defined").await?.is_some() {
+ xml.close().await?;
+ return Ok(Self::IsNotDefined)
+ }
+
xml.maybe_read(&mut time_range, &mut dirty).await?;
xml.maybe_push(&mut prop_filter, &mut dirty).await?;
xml.maybe_push(&mut comp_filter, &mut dirty).await?;
@@ -596,11 +596,17 @@ impl QRead<CompFilterMatch> for CompFilterMatch {
match (&time_range, &prop_filter[..], &comp_filter[..]) {
(None, [], []) => Err(ParsingError::Recoverable),
- _ => Ok(CompFilterMatch { time_range, prop_filter, comp_filter }),
+ _ => Ok(Self::Matches(CompFilterMatch { time_range, prop_filter, comp_filter })),
}
}
}
+impl QRead<CompFilterMatch> for CompFilterMatch {
+ async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
+ unreachable!();
+ }
+}
+
impl QRead<PropFilter> for PropFilter {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
xml.open(CAL_URN, "prop-filter").await?;
@@ -613,22 +619,18 @@ impl QRead<PropFilter> for PropFilter {
impl QRead<PropFilterRules> for PropFilterRules {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- if xml.maybe_open(CAL_URN, "is-not-defined").await?.is_some() {
- xml.close().await?;
- return Ok(Self::IsNotDefined)
- }
- PropFilterMatch::qread(xml).await.map(PropFilterRules::Match)
- }
-}
-
-impl QRead<PropFilterMatch> for PropFilterMatch {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
let mut time_range = None;
let mut time_or_text = None;
let mut param_filter = Vec::new();
loop {
let mut dirty = false;
+
+ if xml.maybe_open(CAL_URN, "is-not-defined").await?.is_some() {
+ xml.close().await?;
+ return Ok(Self::IsNotDefined)
+ }
+
xml.maybe_read(&mut time_range, &mut dirty).await?;
xml.maybe_read(&mut time_or_text, &mut dirty).await?;
xml.maybe_push(&mut param_filter, &mut dirty).await?;
@@ -643,11 +645,17 @@ impl QRead<PropFilterMatch> for PropFilterMatch {
match (&time_range, &time_or_text, &param_filter[..]) {
(None, None, []) => Err(ParsingError::Recoverable),
- _ => Ok(PropFilterMatch { time_range, time_or_text, param_filter }),
+ _ => Ok(PropFilterRules::Match(PropFilterMatch { time_range, time_or_text, param_filter })),
}
}
}
+impl QRead<PropFilterMatch> for PropFilterMatch {
+ async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
+ unreachable!();
+ }
+}
+
impl QRead<ParamFilter> for ParamFilter {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
xml.open(CAL_URN, "param-filter").await?;
@@ -922,4 +930,222 @@ END:VCALENDAR]]></C:calendar-timezone>
let got = deserialize::<CalendarQuery<Calendar>>(src).await;
assert_eq!(got, expected)
}
+
+ #[tokio::test]
+ async fn rfc_calendar_query_res() {
+ let expected = dav::Multistatus::<Calendar, dav::PropValue<Calendar>> {
+ responses: vec![
+ dav::Response {
+ status_or_propstat: dav::StatusOrPropstat::PropStat(
+ dav::Href("http://cal.example.com/bernard/work/abcd2.ics".into()),
+ vec![
+ dav::PropStat {
+ prop: dav::PropValue(vec![
+ dav::Property::GetEtag("\"fffff-abcd2\"".into()),
+ dav::Property::Extension(Property::CalendarData(CalendarDataPayload {
+ mime: None,
+ payload: "BEGIN:VCALENDAR".into(),
+ })),
+ ]),
+ status: dav::Status(http::status::StatusCode::OK),
+ error: None,
+ responsedescription: None,
+ },
+ ],
+ ),
+ error: None,
+ location: None,
+ responsedescription: None,
+ },
+ dav::Response {
+ status_or_propstat: dav::StatusOrPropstat::PropStat(
+ dav::Href("http://cal.example.com/bernard/work/abcd3.ics".into()),
+ vec![
+ dav::PropStat {
+ prop: dav::PropValue(vec![
+ dav::Property::GetEtag("\"fffff-abcd3\"".into()),
+ dav::Property::Extension(Property::CalendarData(CalendarDataPayload {
+ mime: None,
+ payload: "BEGIN:VCALENDAR".into(),
+ })),
+ ]),
+ status: dav::Status(http::status::StatusCode::OK),
+ error: None,
+ responsedescription: None,
+ },
+ ],
+ ),
+ error: None,
+ location: None,
+ responsedescription: None,
+ },
+ ],
+ responsedescription: None,
+ };
+
+ let src = r#"<D:multistatus xmlns:D="DAV:"
+ xmlns:C="urn:ietf:params:xml:ns:caldav">
+ <D:response>
+ <D:href>http://cal.example.com/bernard/work/abcd2.ics</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"fffff-abcd2"</D:getetag>
+ <C:calendar-data>BEGIN:VCALENDAR</C:calendar-data>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ <D:response>
+ <D:href>http://cal.example.com/bernard/work/abcd3.ics</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:getetag>"fffff-abcd3"</D:getetag>
+ <C:calendar-data>BEGIN:VCALENDAR</C:calendar-data>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ </D:multistatus>
+"#;
+
+ let got = deserialize::<dav::Multistatus<Calendar,dav::PropValue<Calendar>>>(src).await;
+ assert_eq!(got, expected)
+ }
+
+ #[tokio::test]
+ async fn rfc_recurring_evt() {
+ let expected = CalendarQuery::<Calendar> {
+ selector: Some(CalendarSelector::Prop(dav::PropName(vec![
+ dav::PropertyRequest::Extension(PropertyRequest::CalendarData(CalendarDataRequest{
+ mime: None,
+ comp: None,
+ recurrence: Some(RecurrenceModifier::LimitRecurrenceSet(LimitRecurrenceSet (
+ Utc.with_ymd_and_hms(2006, 1, 3, 0, 0, 0).unwrap(),
+ Utc.with_ymd_and_hms(2006, 1, 5, 0, 0, 0).unwrap(),
+ ))),
+ limit_freebusy_set: None,
+ })),
+ ]))),
+ filter: Filter(CompFilter {
+ name: Component::VCalendar,
+ additional_rules: Some(CompFilterRules::Matches(CompFilterMatch {
+ prop_filter: vec![],
+ comp_filter: vec![
+ CompFilter {
+ name: Component::VEvent,
+ additional_rules: Some(CompFilterRules::Matches(CompFilterMatch {
+ prop_filter: vec![],
+ comp_filter: vec![],
+ time_range: Some(TimeRange::FullRange(
+ Utc.with_ymd_and_hms(2006, 1, 3, 0, 0, 0).unwrap(),
+ Utc.with_ymd_and_hms(2006, 1, 5, 0, 0, 0).unwrap(),
+ )),
+ })),
+ },
+ ],
+ time_range: None,
+ })),
+ }),
+ timezone: None,
+ };
+
+ let src = r#"
+ <?xml version="1.0" encoding="utf-8" ?>
+ <C:calendar-query xmlns:D="DAV:"
+ xmlns:C="urn:ietf:params:xml:ns:caldav">
+ <D:prop>
+ <C:calendar-data>
+ <C:limit-recurrence-set start="20060103T000000Z"
+ end="20060105T000000Z"/>
+ </C:calendar-data>
+ </D:prop>
+ <C:filter>
+ <C:comp-filter name="VCALENDAR">
+ <C:comp-filter name="VEVENT">
+ <C:time-range start="20060103T000000Z"
+ end="20060105T000000Z"/>
+ </C:comp-filter>
+ </C:comp-filter>
+ </C:filter>
+ </C:calendar-query>"#;
+
+ let got = deserialize::<CalendarQuery<Calendar>>(src).await;
+ assert_eq!(got, expected)
+ }
+
+ #[tokio::test]
+ async fn rfc_pending_todos() {
+ let expected = CalendarQuery::<Calendar> {
+ selector: Some(CalendarSelector::Prop(dav::PropName(vec![
+ dav::PropertyRequest::GetEtag,
+ dav::PropertyRequest::Extension(PropertyRequest::CalendarData(CalendarDataRequest {
+ mime: None,
+ comp: None,
+ recurrence: None,
+ limit_freebusy_set: None,
+ }))
+ ]))),
+ filter: Filter(CompFilter {
+ name: Component::VCalendar,
+ additional_rules: Some(CompFilterRules::Matches(CompFilterMatch {
+ time_range: None,
+ prop_filter: vec![],
+ comp_filter: vec![
+ CompFilter {
+ name: Component::VTodo,
+ additional_rules: Some(CompFilterRules::Matches(CompFilterMatch {
+ time_range: None,
+ comp_filter: vec![],
+ prop_filter: vec![
+ PropFilter {
+ name: ComponentProperty("COMPLETED".into()),
+ additional_rules: Some(PropFilterRules::IsNotDefined),
+ },
+ PropFilter {
+ name: ComponentProperty("STATUS".into()),
+ additional_rules: Some(PropFilterRules::Match(PropFilterMatch {
+ time_range: None,
+ param_filter: vec![],
+ time_or_text: Some(TimeOrText::Text(TextMatch {
+ collation: None,
+ negate_condition: Some(true),
+ text: "CANCELLED".into(),
+ })),
+ })),
+ },
+ ],
+ })),
+ }
+ ],
+ })),
+ }),
+ timezone: None,
+ };
+
+ let src = 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="VTODO">
+ <C:prop-filter name="COMPLETED">
+ <C:is-not-defined/>
+ </C:prop-filter>
+ <C:prop-filter name="STATUS">
+ <C:text-match
+ negate-condition="yes">CANCELLED</C:text-match>
+ </C:prop-filter>
+ </C:comp-filter>
+ </C:comp-filter>
+ </C:filter>
+ </C:calendar-query>"#;
+
+
+ let got = deserialize::<CalendarQuery<Calendar>>(src).await;
+ assert_eq!(got, expected)
+
+ }
}
diff --git a/aero-dav/src/xml.rs b/aero-dav/src/xml.rs
index 2f3b7a6..1f8a6b1 100644
--- a/aero-dav/src/xml.rs
+++ b/aero-dav/src/xml.rs
@@ -113,7 +113,7 @@ impl<T: IRead> Reader<T> {
}
}
- fn parent_has_child(&self) -> bool {
+ pub fn parent_has_child(&self) -> bool {
matches!(self.parents.last(), Some(Event::Start(_)) | None)
}
@@ -238,7 +238,7 @@ impl<T: IRead> Reader<T> {
}
pub async fn open(&mut self, ns: &[u8], key: &str) -> Result<Event<'static>, ParsingError> {
- //println!("try open tag {:?}", key);
+ //println!("try open tag {:?}, on {:?}", key, self.peek());
let evt = match self.peek() {
Event::Empty(_) if self.is_tag(ns, key) => {
// hack to make `prev_attr` works
@@ -253,7 +253,7 @@ impl<T: IRead> Reader<T> {
_ => return Err(ParsingError::Recoverable),
};
- println!("open tag {:?}", evt);
+ //println!("open tag {:?}", evt);
self.parents.push(evt.clone());
Ok(evt)
}
@@ -278,7 +278,7 @@ impl<T: IRead> Reader<T> {
// find stop tag
pub async fn close(&mut self) -> Result<Event<'static>, ParsingError> {
- println!("close tag {:?}", self.parents.last());
+ //println!("close tag {:?}", self.parents.last());
// Handle the empty case
if !self.parent_has_child() {