diff options
author | Quentin Dufour <quentin@deuxfleurs.fr> | 2024-03-08 18:23:23 +0100 |
---|---|---|
committer | Quentin Dufour <quentin@deuxfleurs.fr> | 2024-03-08 18:23:23 +0100 |
commit | 7459f50b5486a137bc90b7e6e04e915d82230e28 (patch) | |
tree | 174a44858b3a539a9e9233feef95b0f4ff2ebfd6 | |
parent | b786573e08c78b672880cd212db45fc58ab82c4c (diff) | |
download | aerogramme-7459f50b5486a137bc90b7e6e04e915d82230e28.tar.gz aerogramme-7459f50b5486a137bc90b7e6e04e915d82230e28.zip |
WIP implem cal decoder
-rw-r--r-- | aero-dav/src/caldecoder.rs | 411 | ||||
-rw-r--r-- | aero-dav/src/calencoder.rs | 13 | ||||
-rw-r--r-- | aero-dav/src/caltypes.rs | 21 | ||||
-rw-r--r-- | aero-dav/src/decoder.rs | 4 | ||||
-rw-r--r-- | aero-dav/src/error.rs | 1 | ||||
-rw-r--r-- | aero-dav/src/xml.rs | 18 |
6 files changed, 439 insertions, 29 deletions
diff --git a/aero-dav/src/caldecoder.rs b/aero-dav/src/caldecoder.rs index 3aae4ad..49d1c9e 100644 --- a/aero-dav/src/caldecoder.rs +++ b/aero-dav/src/caldecoder.rs @@ -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> { - unreachable!(); +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: 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, 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> 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<CalendarQuery<E>> for CalendarQuery<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> { +impl<E: dav::Extension> QRead<CalendarMultiget<E>> for CalendarMultiget<E> { + async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, 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> { - unreachable!(); +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 xml::QRead<PropertyRequest> for PropertyRequest { - async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> { +impl QRead<PropertyRequest> for PropertyRequest { + async fn qread(_xml: &mut Reader<impl IRead>) -> Result<Self, 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) + } +} diff --git a/aero-dav/src/calencoder.rs b/aero-dav/src/calencoder.rs index a25d767..55778db 100644 --- a/aero-dav/src/calencoder.rs +++ b/aero-dav/src/calencoder.rs @@ -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"); diff --git a/aero-dav/src/caltypes.rs b/aero-dav/src/caltypes.rs index 9b9091e..d04c67a 100644 --- a/aero-dav/src/caltypes.rs +++ b/aero-dav/src/caltypes.rs @@ -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), + } + } } diff --git a/aero-dav/src/decoder.rs b/aero-dav/src/decoder.rs index 766d19c..de04dd4 100644 --- a/aero-dav/src/decoder.rs +++ b/aero-dav/src/decoder.rs @@ -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 } diff --git a/aero-dav/src/error.rs b/aero-dav/src/error.rs index 78c6d6b..f1b5cba 100644 --- a/aero-dav/src/error.rs +++ b/aero-dav/src/error.rs @@ -4,6 +4,7 @@ use quick_xml::events::attributes::AttrError; pub enum ParsingError { Recoverable, MissingChild, + MissingAttribute, NamespacePrefixAlreadyUsed, WrongToken, TagNotFound, diff --git a/aero-dav/src/xml.rs b/aero-dav/src/xml.rs index f9e04eb..e021543 100644 --- a/aero-dav/src/xml.rs +++ b/aero-dav/src/xml.rs @@ -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()); |