WIP implem cal decoder

This commit is contained in:
Quentin 2024-03-08 18:23:23 +01:00
parent b786573e08
commit 7459f50b54
Signed by: quentin
GPG key ID: E9602264D639FF68
6 changed files with 445 additions and 35 deletions

View file

@ -1,68 +1,431 @@
use quick_xml::events::Event;
use chrono::NaiveDateTime;
use super::types as dav;
use super::caltypes::*;
use super::xml;
use super::error;
use super::xml::{QRead, IRead, Reader, Node, CAL_URN};
use super::error::ParsingError;
// ---- 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> {
impl<E: dav::Extension> QRead<MkCalendar<E>> for MkCalendar<E> {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
xml.open(CAL_URN, "mkcalendar").await?;
let set = xml.find().await?;
xml.close().await?;
Ok(MkCalendar(set))
}
}
impl<E: dav::Extension, N: Node<N>> QRead<MkCalendarResponse<E,N>> for MkCalendarResponse<E,N> {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, 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> {
impl<E: dav::Extension> QRead<CalendarQuery<E>> for CalendarQuery<E> {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, 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> {
impl<E: dav::Extension> QRead<CalendarMultiget<E>> for CalendarMultiget<E> {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, 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> {
impl QRead<FreeBusyQuery> for FreeBusyQuery {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
// ---- EXTENSIONS ---
impl xml::QRead<Violation> for Violation {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
impl QRead<Violation> for Violation {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl xml::QRead<Property> for Property {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
impl QRead<Property> for Property {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
if xml.maybe_open(CAL_URN, "calendar-description").await?.is_some() {
let lang = xml.prev_attr("xml:lang");
let text = xml.tag_string().await?;
xml.close().await?;
return Ok(Property::CalendarDescription { lang, text })
}
if xml.maybe_open(CAL_URN, "calendar-timezone").await?.is_some() {
let tz = xml.tag_string().await?;
xml.close().await?;
return Ok(Property::CalendarTimezone(tz))
}
if xml.maybe_open(CAL_URN, "supported-calendar-component-set").await?.is_some() {
let comp = xml.collect().await?;
xml.close().await?;
return Ok(Property::SupportedCalendarComponentSet(comp))
}
if xml.maybe_open(CAL_URN, "supported-calendar-data").await?.is_some() {
let mime = xml.collect().await?;
xml.close().await?;
return Ok(Property::SupportedCalendarData(mime))
}
if xml.maybe_open(CAL_URN, "max-resource-size").await?.is_some() {
let sz = xml.tag_string().await?.parse::<u64>()?;
xml.close().await?;
return Ok(Property::MaxResourceSize(sz))
}
if xml.maybe_open(CAL_URN, "max-date-time").await?.is_some() {
let dtstr = xml.tag_string().await?;
let dt = NaiveDateTime::parse_from_str(dtstr.as_str(), ICAL_DATETIME_FMT)?.and_utc();
xml.close().await?;
return Ok(Property::MaxDateTime(dt))
}
if xml.maybe_open(CAL_URN, "max-instances").await?.is_some() {
let sz = xml.tag_string().await?.parse::<u64>()?;
xml.close().await?;
return Ok(Property::MaxInstances(sz))
}
if xml.maybe_open(CAL_URN, "max-attendees-per-instance").await?.is_some() {
let sz = xml.tag_string().await?.parse::<u64>()?;
xml.close().await?;
return Ok(Property::MaxAttendeesPerInstance(sz))
}
if xml.maybe_open(CAL_URN, "supported-collation-set").await?.is_some() {
let cols = xml.collect().await?;
xml.close().await?;
return Ok(Property::SupportedCollationSet(cols))
}
let mut dirty = false;
let mut caldata: Option<CalendarDataPayload> = None;
xml.maybe_read(&mut caldata, &mut dirty).await?;
if let Some(cal) = caldata {
return Ok(Property::CalendarData(cal))
}
Err(ParsingError::Recoverable)
}
}
impl QRead<PropertyRequest> for PropertyRequest {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl xml::QRead<PropertyRequest> for PropertyRequest {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
unreachable!();
}
}
impl xml::QRead<ResourceType> for ResourceType {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
impl QRead<ResourceType> for ResourceType {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
// ---- INNER XML ----
impl xml::QRead<SupportedCollation> for SupportedCollation {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
impl QRead<SupportedCollation> for SupportedCollation {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
xml.open(CAL_URN, "supported-collation").await?;
let col = Collation::new(xml.tag_string().await?);
xml.close().await?;
Ok(SupportedCollation(col))
}
}
impl QRead<CalendarDataPayload> for CalendarDataPayload {
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 payload = xml.tag_string().await?;
xml.close().await?;
Ok(CalendarDataPayload { mime, payload })
}
}
impl QRead<CalendarDataSupport> for CalendarDataSupport {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
let ct = xml.prev_attr("content-type");
let vs = xml.prev_attr("version");
match (ct, vs) {
(Some(content_type), Some(version)) => Ok(Self { content_type, version }),
_ => Err(ParsingError::Recoverable),
}
}
}
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);
loop {
let mut dirty = false;
xml.maybe_read(&mut comp, &mut dirty).await?;
xml.maybe_read(&mut recurrence, &mut dirty).await?;
xml.maybe_read(&mut limit_freebusy_set, &mut dirty).await?;
if !dirty {
match xml.peek() {
Event::End(_) => break,
_ => xml.skip().await?,
};
}
}
xml.close().await?;
Ok(Self { mime, comp, recurrence, limit_freebusy_set })
}
}
impl QRead<CalendarDataEmpty> for CalendarDataEmpty {
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();
xml.close().await?;
Ok(Self(mime))
}
}
impl QRead<Comp> for Comp {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
xml.open(CAL_URN, "comp").await?;
let name = Component::new(xml.prev_attr("name").ok_or(ParsingError::MissingAttribute)?);
let additional_rules = Box::pin(xml.maybe_find()).await?;
xml.close().await?;
Ok(Self { name, additional_rules })
}
}
impl QRead<CompInner> for CompInner {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
let (mut prop_kind, mut comp_kind) = (None, None);
loop {
let mut dirty = false;
xml.maybe_read(&mut prop_kind, &mut dirty).await?;
xml.maybe_read(&mut comp_kind, &mut dirty).await?;
if !dirty {
match xml.peek() {
Event::End(_) => break,
_ => xml.skip().await?,
};
}
};
match (prop_kind, comp_kind) {
(Some(prop_kind), Some(comp_kind)) => Ok(Self { prop_kind, comp_kind }),
_ => Err(ParsingError::MissingChild),
}
}
}
impl QRead<CompSupport> for CompSupport {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
xml.open(CAL_URN, "comp").await?;
let inner = Component::new(xml.prev_attr("name").ok_or(ParsingError::MissingAttribute)?);
xml.close().await?;
Ok(Self(inner))
}
}
impl QRead<CompKind> for CompKind {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
let mut comp = Vec::new();
loop {
let mut dirty = false;
if xml.maybe_open(CAL_URN, "allcomp").await?.is_some() {
xml.close().await?;
return Ok(CompKind::AllComp)
}
xml.maybe_push(&mut comp, &mut dirty).await?;
if !dirty {
match xml.peek() {
Event::End(_) => break,
_ => xml.skip().await?,
};
}
}
Ok(CompKind::Comp(comp))
}
}
impl QRead<PropKind> for PropKind {
async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
let mut prop = Vec::new();
loop {
let mut dirty = false;
if xml.maybe_open(CAL_URN, "allprop").await?.is_some() {
xml.close().await?;
return Ok(PropKind::AllProp)
}
xml.maybe_push(&mut prop, &mut dirty).await?;
if !dirty {
match xml.peek() {
Event::End(_) => break,
_ => xml.skip().await?,
};
}
}
Ok(PropKind::Prop(prop))
}
}
impl QRead<RecurrenceModifier> for RecurrenceModifier {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<Expand> for Expand {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<LimitRecurrenceSet> for LimitRecurrenceSet {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<LimitFreebusySet> for LimitFreebusySet {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl<E: dav::Extension> QRead<CalendarSelector<E>> for CalendarSelector<E> {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<CompFilter> for CompFilter {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<CompFilterRules> for CompFilterRules {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
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> {
unreachable!();
}
}
impl QRead<PropFilterRules> for PropFilterRules {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<PropFilterMatch> for PropFilterMatch {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<TimeOrText> for TimeOrText {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<TextMatch> for TextMatch {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<ParamFilterMatch> for ParamFilterMatch {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<TimeZone> for TimeZone {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<Filter> for Filter {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<TimeRange> for TimeRange {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
impl QRead<CalProp> for CalProp {
async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
unreachable!();
}
}
#[cfg(test)]
mod tests {
use super::*;
//use chrono::{FixedOffset, TimeZone};
use crate::realization::Calendar;
//use quick_reader::NsReader;
async fn deserialize<T: Node<T>>(src: &str) -> T {
let mut rdr = Reader::new(quick_xml::NsReader::from_reader(src.as_bytes())).await.unwrap();
rdr.find().await.unwrap()
}
#[tokio::test]
async fn basic_mkcalendar() {
let expected = MkCalendar(dav::Set(dav::PropValue(vec![
dav::Property::DisplayName("Lisa's Events".into()),
])));
let src = r#"
<?xml version="1.0" encoding="utf-8" ?>
<C:mkcalendar xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:set>
<D:prop>
<D:displayname>Lisa's Events</D:displayname>
</D:prop>
</D:set>
</C:mkcalendar>
"#;
let got = deserialize::<MkCalendar<Calendar>>(src).await;
assert_eq!(got, expected)
}
}

View file

@ -5,7 +5,6 @@ use super::caltypes::*;
use super::xml::{Node, QWrite, IWrite, Writer};
use super::types::Extension;
const ICAL_DATETIME_FMT: &str = "%Y%m%dT%H%M%SZ";
// ==================== Calendar Types Serialization =========================
@ -300,6 +299,12 @@ impl QWrite for Collation {
}
}
impl QWrite for CalendarDataSupport {
async fn qwrite(&self, _xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
unreachable!();
}
}
impl QWrite for CalendarDataPayload {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
let mut start = xml.create_cal_element("calendar-data");
@ -348,6 +353,12 @@ impl QWrite for CalendarDataEmpty {
}
}
impl QWrite for CompInner {
async fn qwrite(&self, _xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
unreachable!();
}
}
impl QWrite for Comp {
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
let mut start = xml.create_cal_element("comp");

View file

@ -4,6 +4,8 @@ use chrono::{DateTime,Utc};
use super::types as dav;
use super::xml;
pub const ICAL_DATETIME_FMT: &str = "%Y%m%dT%H%M%SZ";
//@FIXME ACL (rfc3744) is missing, required
//@FIXME Versioning (rfc3253) is missing, required
//@FIXME WebDAV sync (rfc6578) is missing, optional
@ -1418,6 +1420,18 @@ impl Component {
Self::Unknown(c) => c,
}
}
pub fn new(v: String) -> Self {
match v.as_str() {
"VCALENDAR" => Self::VCalendar,
"VJOURNAL" => Self::VJournal,
"VFREEBUSY" => Self::VFreeBusy,
"VEVENT" => Self::VEvent,
"VTODO" => Self::VTodo,
"VALARM" => Self::VAlarm,
"VTIMEZONE" => Self::VTimeZone,
_ => Self::Unknown(v),
}
}
}
/// name="VERSION", name="SUMMARY", etc.
@ -1450,4 +1464,11 @@ impl Collation {
Self::Unknown(c) => c.as_str(),
}
}
pub fn new(v: String) -> Self {
match v.as_str() {
"i;ascii-casemap" => Self::AsciiCaseMap,
"i;octet" => Self::Octet,
_ => Self::Unknown(v),
}
}
}

View file

@ -551,7 +551,9 @@ impl QRead<LockScope> for LockScope {
if xml.maybe_open(DAV_URN, "exclusive").await?.is_some() {
xml.close().await?;
break LockScope::Exclusive
} else if xml.maybe_open(DAV_URN, "shared").await?.is_some() {
}
if xml.maybe_open(DAV_URN, "shared").await?.is_some() {
xml.close().await?;
break LockScope::Shared
}

View file

@ -4,6 +4,7 @@ use quick_xml::events::attributes::AttrError;
pub enum ParsingError {
Recoverable,
MissingChild,
MissingAttribute,
NamespacePrefixAlreadyUsed,
WrongToken,
TagNotFound,

View file

@ -55,6 +55,7 @@ impl<T: IWrite> Writer<T> {
pub struct Reader<T: IRead> {
pub rdr: NsReader<T>,
cur: Event<'static>,
prev: Event<'static>,
parents: Vec<Event<'static>>,
buf: Vec<u8>,
}
@ -63,8 +64,9 @@ impl<T: IRead> Reader<T> {
let mut buf: Vec<u8> = vec![];
let cur = rdr.read_event_into_async(&mut buf).await?.into_owned();
let parents = vec![];
let prev = Event::Eof;
buf.clear();
Ok(Self { cur, parents, rdr, buf })
Ok(Self { cur, prev, parents, rdr, buf })
}
/// read one more tag
@ -72,8 +74,8 @@ impl<T: IRead> Reader<T> {
async fn next(&mut self) -> Result<Event<'static>, ParsingError> {
let evt = self.rdr.read_event_into_async(&mut self.buf).await?.into_owned();
self.buf.clear();
let old_evt = std::mem::replace(&mut self.cur, evt);
Ok(old_evt)
self.prev = std::mem::replace(&mut self.cur, evt);
Ok(self.prev.clone())
}
/// skip a node at current level
@ -252,6 +254,16 @@ impl<T: IRead> Reader<T> {
}
}
pub fn prev_attr(&self, attr: &str) -> Option<String> {
match &self.prev {
Event::Start(bs) | Event::Empty(bs) => match bs.try_get_attribute(attr) {
Ok(Some(attr)) => attr.decode_and_unescape_value(&self.rdr).ok().map(|v| v.into_owned()),
_ => None,
}
_ => None,
}
}
// find stop tag
pub async fn close(&mut self) -> Result<Event<'static>, ParsingError> {
//println!("close tag {:?}", self.parents.last());