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> {
|
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
|
||||||
xml.open(CAL_URN, "calendar-data").await?;
|
xml.open(CAL_URN, "calendar-data").await?;
|
||||||
let mime = CalendarDataSupport::qread(xml).await.ok();
|
let mime = CalendarDataSupport::qread(xml).await.ok();
|
||||||
|
|
||||||
let (mut comp, mut recurrence, mut limit_freebusy_set) = (None, None, None);
|
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 {
|
loop {
|
||||||
let mut dirty = false;
|
let mut dirty = false;
|
||||||
xml.maybe_read(&mut comp, &mut dirty).await?;
|
xml.maybe_read(&mut comp, &mut dirty).await?;
|
||||||
|
@ -565,16 +569,6 @@ impl QRead<CompFilter> for CompFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QRead<CompFilterRules> for CompFilterRules {
|
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> {
|
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
|
||||||
let mut time_range = None;
|
let mut time_range = None;
|
||||||
let mut prop_filter = Vec::new();
|
let mut prop_filter = Vec::new();
|
||||||
|
@ -582,6 +576,12 @@ impl QRead<CompFilterMatch> for CompFilterMatch {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut dirty = false;
|
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_range, &mut dirty).await?;
|
||||||
xml.maybe_push(&mut prop_filter, &mut dirty).await?;
|
xml.maybe_push(&mut prop_filter, &mut dirty).await?;
|
||||||
xml.maybe_push(&mut comp_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[..]) {
|
match (&time_range, &prop_filter[..], &comp_filter[..]) {
|
||||||
(None, [], []) => Err(ParsingError::Recoverable),
|
(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 {
|
impl QRead<PropFilter> for PropFilter {
|
||||||
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
|
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
|
||||||
xml.open(CAL_URN, "prop-filter").await?;
|
xml.open(CAL_URN, "prop-filter").await?;
|
||||||
|
@ -612,16 +618,6 @@ impl QRead<PropFilter> for PropFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QRead<PropFilterRules> for PropFilterRules {
|
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> {
|
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
|
||||||
let mut time_range = None;
|
let mut time_range = None;
|
||||||
let mut time_or_text = None;
|
let mut time_or_text = None;
|
||||||
|
@ -629,6 +625,12 @@ impl QRead<PropFilterMatch> for PropFilterMatch {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut dirty = false;
|
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_range, &mut dirty).await?;
|
||||||
xml.maybe_read(&mut time_or_text, &mut dirty).await?;
|
xml.maybe_read(&mut time_or_text, &mut dirty).await?;
|
||||||
xml.maybe_push(&mut param_filter, &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[..]) {
|
match (&time_range, &time_or_text, ¶m_filter[..]) {
|
||||||
(None, None, []) => Err(ParsingError::Recoverable),
|
(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 {
|
impl QRead<ParamFilter> for ParamFilter {
|
||||||
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
|
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
|
||||||
xml.open(CAL_URN, "param-filter").await?;
|
xml.open(CAL_URN, "param-filter").await?;
|
||||||
|
@ -922,4 +930,222 @@ END:VCALENDAR]]></C:calendar-timezone>
|
||||||
let got = deserialize::<CalendarQuery<Calendar>>(src).await;
|
let got = deserialize::<CalendarQuery<Calendar>>(src).await;
|
||||||
assert_eq!(got, expected)
|
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)
|
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> {
|
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() {
|
let evt = match self.peek() {
|
||||||
Event::Empty(_) if self.is_tag(ns, key) => {
|
Event::Empty(_) if self.is_tag(ns, key) => {
|
||||||
// hack to make `prev_attr` works
|
// hack to make `prev_attr` works
|
||||||
|
@ -253,7 +253,7 @@ impl<T: IRead> Reader<T> {
|
||||||
_ => return Err(ParsingError::Recoverable),
|
_ => return Err(ParsingError::Recoverable),
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("open tag {:?}", evt);
|
//println!("open tag {:?}", evt);
|
||||||
self.parents.push(evt.clone());
|
self.parents.push(evt.clone());
|
||||||
Ok(evt)
|
Ok(evt)
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@ impl<T: IRead> Reader<T> {
|
||||||
|
|
||||||
// find stop tag
|
// find stop tag
|
||||||
pub async fn close(&mut self) -> Result<Event<'static>, ParsingError> {
|
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
|
// Handle the empty case
|
||||||
if !self.parent_has_child() {
|
if !self.parent_has_child() {
|
||||||
|
|
Loading…
Reference in a new issue