fix caldecoder + xml
This commit is contained in:
parent
442433d70b
commit
98adb1e20d
2 changed files with 253 additions and 27 deletions
|
@ -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?;
|
||||
|
@ -565,16 +569,6 @@ 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();
|
||||
|
@ -582,6 +576,12 @@ impl QRead<CompFilterMatch> for CompFilterMatch {
|
|||
|
||||
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?;
|
||||
|
@ -612,16 +618,6 @@ 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;
|
||||
|
@ -629,6 +625,12 @@ impl QRead<PropFilterMatch> for PropFilterMatch {
|
|||
|
||||
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, ¶m_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)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue