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::runtime::Runtime;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
// Split this file
const tokens: [&str; 63] = [ const tokens: [&str; 63] = [
"0", "0",
"1", "1",
@ -125,6 +126,9 @@ impl Tag {
#[derive(Arbitrary)] #[derive(Arbitrary)]
enum XmlNode { 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>), Node(Tag, Vec<Self>),
Number(u64), Number(u64),
Text(Token), Text(Token),

View file

@ -1,9 +1,39 @@
//use super::types as dav; use super::types as dav;
use super::caltypes::*; use super::caltypes::*;
use super::xml; use super::xml;
use super::error; use super::error;
// ---- ROOT ELEMENTS --- // ---- 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 --- // ---- EXTENSIONS ---
impl xml::QRead<Violation> for Violation { impl xml::QRead<Violation> for Violation {
@ -31,3 +61,8 @@ impl xml::QRead<ResourceType> for ResourceType {
} }
// ---- INNER XML ---- // ---- 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::types as dav;
use crate::realization::Calendar; use crate::realization::Calendar;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use chrono::{Utc,TimeZone,DateTime}; use chrono::{Utc,TimeZone};
async fn serialize(elem: &impl QWrite) -> String { async fn serialize(elem: &impl QWrite) -> String {
let mut buffer = Vec::new(); 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 { let propfind: PropFind<E> = loop {
// allprop // allprop
if let Some(_) = xml.maybe_open(DAV_URN, "allprop").await? { if let Some(_) = xml.maybe_open(DAV_URN, "allprop").await? {
let includ = xml.maybe_find::<Include<E>>().await?;
xml.close().await?; xml.close().await?;
let includ = xml.maybe_find::<Include<E>>().await?;
break PropFind::AllProp(includ) break PropFind::AllProp(includ)
} }
@ -594,8 +594,9 @@ impl QRead<Href> for Href {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use chrono::{FixedOffset, DateTime, TimeZone, Utc}; use chrono::{FixedOffset, TimeZone};
use crate::realization::Core; use crate::realization::Core;
use quick_xml::reader::NsReader;
#[tokio::test] #[tokio::test]
async fn basic_propfind_propname() { async fn basic_propfind_propname() {
@ -910,7 +911,7 @@ mod tests {
Property::GetContentType("text/html".into()), Property::GetContentType("text/html".into()),
Property::GetEtag(r#""zzyzx""#.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::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![ Property::SupportedLock(vec![
LockEntry { LockEntry {
lockscope: LockScope::Exclusive, lockscope: LockScope::Exclusive,

View file

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

View file

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