Fixed some parsing bugs

This commit is contained in:
Quentin 2024-03-08 11:34:24 +01:00
parent b9f32d720a
commit 4d65366ff3
Signed by: quentin
GPG Key ID: E9602264D639FF68
6 changed files with 206 additions and 152 deletions

View File

@ -9,6 +9,7 @@ use quick_xml::reader::NsReader;
use tokio::runtime::Runtime;
use tokio::io::AsyncWriteExt;
// Split this file
const tokens: [&str; 63] = [
"0",
"1",
@ -125,6 +126,9 @@ impl Tag {
#[derive(Arbitrary)]
enum XmlNode {
//@FIXME: build RFC3339 and RFC822 Dates with chrono based on timestamps
//@FIXME: add small numbers
//@FIXME: add http status code
Node(Tag, Vec<Self>),
Number(u64),
Text(Token),

View File

@ -1,9 +1,39 @@
//use super::types as dav;
use super::types as dav;
use super::caltypes::*;
use super::xml;
use super::error;
// ---- ROOT ELEMENTS ---
impl<E: dav::Extension> xml::QRead<MkCalendar<E>> for MkCalendar<E> {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
unreachable!();
}
}
impl<E: dav::Extension, N: xml::Node<N>> xml::QRead<MkCalendarResponse<E,N>> for MkCalendarResponse<E,N> {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
unreachable!();
}
}
impl<E: dav::Extension> xml::QRead<CalendarQuery<E>> for CalendarQuery<E> {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
unreachable!();
}
}
impl<E: dav::Extension> xml::QRead<CalendarMultiget<E>> for CalendarMultiget<E> {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
unreachable!();
}
}
impl xml::QRead<FreeBusyQuery> for FreeBusyQuery {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
unreachable!();
}
}
// ---- EXTENSIONS ---
impl xml::QRead<Violation> for Violation {
@ -31,3 +61,8 @@ impl xml::QRead<ResourceType> for ResourceType {
}
// ---- INNER XML ----
impl xml::QRead<SupportedCollation> for SupportedCollation {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
unreachable!();
}
}

View File

@ -666,7 +666,7 @@ mod tests {
use crate::types as dav;
use crate::realization::Calendar;
use tokio::io::AsyncWriteExt;
use chrono::{Utc,TimeZone,DateTime};
use chrono::{Utc,TimeZone};
async fn serialize(elem: &impl QWrite) -> String {
let mut buffer = Vec::new();

View File

@ -23,8 +23,8 @@ impl<E: Extension> QRead<PropFind<E>> for PropFind<E> {
let propfind: PropFind<E> = loop {
// allprop
if let Some(_) = xml.maybe_open(DAV_URN, "allprop").await? {
let includ = xml.maybe_find::<Include<E>>().await?;
xml.close().await?;
let includ = xml.maybe_find::<Include<E>>().await?;
break PropFind::AllProp(includ)
}
@ -594,8 +594,9 @@ impl QRead<Href> for Href {
#[cfg(test)]
mod tests {
use super::*;
use chrono::{FixedOffset, DateTime, TimeZone, Utc};
use chrono::{FixedOffset, TimeZone};
use crate::realization::Core;
use quick_xml::reader::NsReader;
#[tokio::test]
async fn basic_propfind_propname() {
@ -910,7 +911,7 @@ mod tests {
Property::GetContentType("text/html".into()),
Property::GetEtag(r#""zzyzx""#.into()),
Property::GetLastModified(FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(1998, 01, 12, 09, 25, 56).unwrap()),
//Property::ResourceType(vec![]),
Property::ResourceType(vec![]),
Property::SupportedLock(vec![
LockEntry {
lockscope: LockScope::Exclusive,

View File

@ -633,6 +633,7 @@ impl<E: Extension> QWrite for Violation<E> {
#[cfg(test)]
mod tests {
use super::*;
use super::super::xml;
use crate::realization::Core;
use tokio::io::AsyncWriteExt;
@ -653,43 +654,47 @@ mod tests {
return got.into()
}
async fn deserialize<T: xml::Node<T>>(src: &str) -> T {
let mut rdr = xml::Reader::new(quick_xml::reader::NsReader::from_reader(src.as_bytes())).await.unwrap();
rdr.find().await.unwrap()
}
#[tokio::test]
async fn basic_href() {
let orig = Href("/SOGo/dav/so/".into());
let got = serialize(
&Href("/SOGo/dav/so/".into())
).await;
let got = serialize(&orig).await;
let expected = r#"<D:href xmlns:D="DAV:">/SOGo/dav/so/</D:href>"#;
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
assert_eq!(deserialize::<Href>(got.as_str()).await, orig)
}
#[tokio::test]
async fn basic_multistatus() {
let got = serialize(
&Multistatus::<Core, PropName<Core>> {
responses: vec![],
responsedescription: Some(ResponseDescription("Hello world".into()))
},
).await;
let orig = Multistatus::<Core, PropName<Core>> {
responses: vec![],
responsedescription: Some(ResponseDescription("Hello world".into()))
};
let got = serialize(&orig).await;
let expected = r#"<D:multistatus xmlns:D="DAV:">
<D:responsedescription>Hello world</D:responsedescription>
</D:multistatus>"#;
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
assert_eq!(deserialize::<Multistatus::<Core, PropName<Core>>>(got.as_str()).await, orig)
}
#[tokio::test]
async fn rfc_error_delete_locked() {
let got = serialize(
&Error::<Core>(vec![
let orig = Error::<Core>(vec![
Violation::LockTokenSubmitted(vec![
Href("/locked/".into())
])
]),
).await;
]);
let got = serialize(&orig).await;
let expected = r#"<D:error xmlns:D="DAV:">
<D:lock-token-submitted>
@ -698,72 +703,74 @@ mod tests {
</D:error>"#;
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
assert_eq!(deserialize::<Error<Core>>(got.as_str()).await, orig)
}
#[tokio::test]
async fn rfc_propname_req() {
let got = serialize(
&PropFind::<Core>::PropName,
).await;
let orig = PropFind::<Core>::PropName;
let got = serialize(&orig).await;
let expected = r#"<D:propfind xmlns:D="DAV:">
<D:propname/>
</D:propfind>"#;
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
assert_eq!(deserialize::<PropFind::<Core>>(got.as_str()).await, orig)
}
#[tokio::test]
async fn rfc_propname_res() {
let got = serialize(
&Multistatus::<Core, PropName<Core>> {
responses: vec![
Response {
status_or_propstat: StatusOrPropstat::PropStat(
Href("http://www.example.com/container/".into()),
vec![PropStat {
prop: PropName(vec![
PropertyRequest::CreationDate,
PropertyRequest::DisplayName,
PropertyRequest::ResourceType,
PropertyRequest::SupportedLock,
]),
status: Status(http::status::StatusCode::OK),
error: None,
responsedescription: None,
}]
),
error: None,
responsedescription: None,
location: None,
},
Response {
status_or_propstat: StatusOrPropstat::PropStat(
Href("http://www.example.com/container/front.html".into()),
vec![PropStat {
prop: PropName(vec![
PropertyRequest::CreationDate,
PropertyRequest::DisplayName,
PropertyRequest::GetContentLength,
PropertyRequest::GetContentType,
PropertyRequest::GetEtag,
PropertyRequest::GetLastModified,
PropertyRequest::ResourceType,
PropertyRequest::SupportedLock,
]),
status: Status(http::status::StatusCode::OK),
error: None,
responsedescription: None,
}
]),
error: None,
responsedescription: None,
location: None,
},
],
responsedescription: None,
},
).await;
let orig = Multistatus::<Core, PropName<Core>> {
responses: vec![
Response {
status_or_propstat: StatusOrPropstat::PropStat(
Href("http://www.example.com/container/".into()),
vec![PropStat {
prop: PropName(vec![
PropertyRequest::CreationDate,
PropertyRequest::DisplayName,
PropertyRequest::ResourceType,
PropertyRequest::SupportedLock,
]),
status: Status(http::status::StatusCode::OK),
error: None,
responsedescription: None,
}]
),
error: None,
responsedescription: None,
location: None,
},
Response {
status_or_propstat: StatusOrPropstat::PropStat(
Href("http://www.example.com/container/front.html".into()),
vec![PropStat {
prop: PropName(vec![
PropertyRequest::CreationDate,
PropertyRequest::DisplayName,
PropertyRequest::GetContentLength,
PropertyRequest::GetContentType,
PropertyRequest::GetEtag,
PropertyRequest::GetLastModified,
PropertyRequest::ResourceType,
PropertyRequest::SupportedLock,
]),
status: Status(http::status::StatusCode::OK),
error: None,
responsedescription: None,
}
]),
error: None,
responsedescription: None,
location: None,
},
],
responsedescription: None,
};
let got = serialize(&orig).await;
let expected = r#"<D:multistatus xmlns:D="DAV:">
<D:response>
@ -798,100 +805,102 @@ mod tests {
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
assert_eq!(deserialize::<Multistatus::<Core, PropName<Core>>>(got.as_str()).await, orig)
}
#[tokio::test]
async fn rfc_allprop_req() {
let got = serialize(
&PropFind::<Core>::AllProp(None),
).await;
let orig = PropFind::<Core>::AllProp(None);
let got = serialize(&orig).await;
let expected = r#"<D:propfind xmlns:D="DAV:">
<D:allprop/>
</D:propfind>"#;
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
assert_eq!(deserialize::<PropFind::<Core>>(got.as_str()).await, orig)
}
#[tokio::test]
async fn rfc_allprop_res() {
use chrono::{DateTime,FixedOffset,TimeZone};
let got = serialize(
&Multistatus::<Core, PropValue<Core>> {
responses: vec![
Response {
status_or_propstat: StatusOrPropstat::PropStat(
Href("/container/".into()),
vec![PropStat {
prop: PropValue(vec![
Property::CreationDate(FixedOffset::west_opt(8 * 3600)
.unwrap()
.with_ymd_and_hms(1997, 12, 1, 17, 42, 21)
.unwrap()),
Property::DisplayName("Example collection".into()),
Property::ResourceType(vec![ResourceType::Collection]),
Property::SupportedLock(vec![
LockEntry {
lockscope: LockScope::Exclusive,
locktype: LockType::Write,
},
LockEntry {
lockscope: LockScope::Shared,
locktype: LockType::Write,
},
]),
use chrono::{FixedOffset,TimeZone};
let orig = Multistatus::<Core, PropValue<Core>> {
responses: vec![
Response {
status_or_propstat: StatusOrPropstat::PropStat(
Href("/container/".into()),
vec![PropStat {
prop: PropValue(vec![
Property::CreationDate(FixedOffset::west_opt(8 * 3600)
.unwrap()
.with_ymd_and_hms(1997, 12, 1, 17, 42, 21)
.unwrap()),
Property::DisplayName("Example collection".into()),
Property::ResourceType(vec![ResourceType::Collection]),
Property::SupportedLock(vec![
LockEntry {
lockscope: LockScope::Exclusive,
locktype: LockType::Write,
},
LockEntry {
lockscope: LockScope::Shared,
locktype: LockType::Write,
},
]),
status: Status(http::status::StatusCode::OK),
error: None,
responsedescription: None,
}]
),
]),
status: Status(http::status::StatusCode::OK),
error: None,
responsedescription: None,
location: None,
},
Response {
status_or_propstat: StatusOrPropstat::PropStat(
Href("/container/front.html".into()),
vec![PropStat {
prop: PropValue(vec![
Property::CreationDate(FixedOffset::west_opt(8 * 3600)
.unwrap()
.with_ymd_and_hms(1997, 12, 1, 18, 27, 21)
.unwrap()),
Property::DisplayName("Example HTML resource".into()),
Property::GetContentLength(4525),
Property::GetContentType("text/html".into()),
Property::GetEtag(r#""zzyzx""#.into()),
Property::GetLastModified(FixedOffset::east_opt(0)
.unwrap()
.with_ymd_and_hms(1998, 1, 12, 9, 25, 56)
.unwrap()),
Property::ResourceType(vec![]),
Property::SupportedLock(vec![
LockEntry {
lockscope: LockScope::Exclusive,
locktype: LockType::Write,
},
LockEntry {
lockscope: LockScope::Shared,
locktype: LockType::Write,
},
]),
}]
),
error: None,
responsedescription: None,
location: None,
},
Response {
status_or_propstat: StatusOrPropstat::PropStat(
Href("/container/front.html".into()),
vec![PropStat {
prop: PropValue(vec![
Property::CreationDate(FixedOffset::west_opt(8 * 3600)
.unwrap()
.with_ymd_and_hms(1997, 12, 1, 18, 27, 21)
.unwrap()),
Property::DisplayName("Example HTML resource".into()),
Property::GetContentLength(4525),
Property::GetContentType("text/html".into()),
Property::GetEtag(r#""zzyzx""#.into()),
Property::GetLastModified(FixedOffset::east_opt(0)
.unwrap()
.with_ymd_and_hms(1998, 1, 12, 9, 25, 56)
.unwrap()),
Property::ResourceType(vec![]),
Property::SupportedLock(vec![
LockEntry {
lockscope: LockScope::Exclusive,
locktype: LockType::Write,
},
LockEntry {
lockscope: LockScope::Shared,
locktype: LockType::Write,
},
]),
status: Status(http::status::StatusCode::OK),
error: None,
responsedescription: None,
}]
),
]),
status: Status(http::status::StatusCode::OK),
error: None,
responsedescription: None,
location: None,
},
],
responsedescription: None,
}
).await;
}]
),
error: None,
responsedescription: None,
location: None,
},
],
responsedescription: None,
};
let got = serialize(&orig).await;
let expected = r#"<D:multistatus xmlns:D="DAV:">
<D:response>
@ -961,16 +970,17 @@ mod tests {
</D:multistatus>"#;
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
assert_eq!(deserialize::<Multistatus::<Core, PropValue<Core>>>(got.as_str()).await, orig)
}
#[tokio::test]
async fn rfc_allprop_include() {
let got = serialize(
&PropFind::<Core>::AllProp(Some(Include(vec![
PropertyRequest::DisplayName,
PropertyRequest::ResourceType,
]))),
).await;
let orig = PropFind::<Core>::AllProp(Some(Include(vec![
PropertyRequest::DisplayName,
PropertyRequest::ResourceType,
])));
let got = serialize(&orig).await;
let expected = r#"<D:propfind xmlns:D="DAV:">
<D:allprop/>
@ -981,6 +991,7 @@ mod tests {
</D:propfind>"#;
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
assert_eq!(deserialize::<PropFind::<Core>>(got.as_str()).await, orig)
}
#[tokio::test]

View File

@ -79,7 +79,7 @@ impl<T: IRead> Reader<T> {
/// skip a node at current level
/// I would like to make this one private but not ready
pub async fn skip(&mut self) -> Result<Event<'static>, ParsingError> {
//println!("skipping inside node {:?}", self.parents.last());
//println!("skipping inside node {:?} value {:?}", self.parents.last(), self.cur);
match &self.cur {
Event::Start(b) => {
let _span = self.rdr.read_to_end_into_async(b.to_end().name(), &mut self.buf).await?;
@ -212,8 +212,10 @@ impl<T: IRead> Reader<T> {
}
pub async fn collect<N: Node<N>>(&mut self) -> Result<Vec<N>, ParsingError> {
self.ensure_parent_has_child()?;
let mut acc = Vec::new();
if !self.parent_has_child() {
return Ok(acc)
}
loop {
match N::qread(self).await {
@ -230,6 +232,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);
let evt = match self.peek() {
Event::Empty(_) if self.is_tag(ns, key) => self.cur.clone(),
Event::Start(_) if self.is_tag(ns, key) => self.next().await?,