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 xml::QRead> for MkCalendar { - async fn qread(_xml: &mut xml::Reader) -> Result { +impl QRead> for MkCalendar { + async fn qread(xml: &mut Reader) -> Result { + xml.open(CAL_URN, "mkcalendar").await?; + let set = xml.find().await?; + xml.close().await?; + Ok(MkCalendar(set)) + } +} + +impl> QRead> for MkCalendarResponse { + async fn qread(_xml: &mut Reader) -> Result { unreachable!(); } } -impl> xml::QRead> for MkCalendarResponse { - async fn qread(_xml: &mut xml::Reader) -> Result { +impl QRead> for CalendarQuery { + async fn qread(_xml: &mut Reader) -> Result { unreachable!(); } } -impl xml::QRead> for CalendarQuery { - async fn qread(_xml: &mut xml::Reader) -> Result { +impl QRead> for CalendarMultiget { + async fn qread(_xml: &mut Reader) -> Result { unreachable!(); } } -impl xml::QRead> for CalendarMultiget { - async fn qread(_xml: &mut xml::Reader) -> Result { - unreachable!(); - } -} - -impl xml::QRead for FreeBusyQuery { - async fn qread(_xml: &mut xml::Reader) -> Result { +impl QRead for FreeBusyQuery { + async fn qread(_xml: &mut Reader) -> Result { unreachable!(); } } // ---- EXTENSIONS --- -impl xml::QRead for Violation { - async fn qread(_xml: &mut xml::Reader) -> Result { +impl QRead for Violation { + async fn qread(_xml: &mut Reader) -> Result { unreachable!(); } } -impl xml::QRead for Property { - async fn qread(_xml: &mut xml::Reader) -> Result { +impl QRead for Property { + async fn qread(xml: &mut Reader) -> Result { + 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::()?; + 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::()?; + 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::()?; + 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 = None; + xml.maybe_read(&mut caldata, &mut dirty).await?; + if let Some(cal) = caldata { + return Ok(Property::CalendarData(cal)) + } + + Err(ParsingError::Recoverable) + } +} + +impl QRead for PropertyRequest { + async fn qread(_xml: &mut Reader) -> Result { unreachable!(); } } -impl xml::QRead for PropertyRequest { - async fn qread(_xml: &mut xml::Reader) -> Result { - unreachable!(); - } -} - -impl xml::QRead for ResourceType { - async fn qread(_xml: &mut xml::Reader) -> Result { +impl QRead for ResourceType { + async fn qread(_xml: &mut Reader) -> Result { unreachable!(); } } // ---- INNER XML ---- -impl xml::QRead for SupportedCollation { - async fn qread(_xml: &mut xml::Reader) -> Result { +impl QRead for SupportedCollation { + async fn qread(xml: &mut Reader) -> Result { + xml.open(CAL_URN, "supported-collation").await?; + let col = Collation::new(xml.tag_string().await?); + xml.close().await?; + Ok(SupportedCollation(col)) + } +} + +impl QRead for CalendarDataPayload { + async fn qread(xml: &mut Reader) -> Result { + 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 for CalendarDataSupport { + async fn qread(xml: &mut Reader) -> Result { + 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 for CalendarDataRequest { + async fn qread(xml: &mut Reader) -> Result { + 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 for CalendarDataEmpty { + async fn qread(xml: &mut Reader) -> Result { + xml.open(CAL_URN, "calendar-data").await?; + let mime = CalendarDataSupport::qread(xml).await.ok(); + xml.close().await?; + Ok(Self(mime)) + } +} + +impl QRead for Comp { + async fn qread(xml: &mut Reader) -> Result { + 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 for CompInner { + async fn qread(xml: &mut Reader) -> Result { + 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 for CompSupport { + async fn qread(xml: &mut Reader) -> Result { + 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 for CompKind { + async fn qread(xml: &mut Reader) -> Result { + 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 for PropKind { + async fn qread(xml: &mut Reader) -> Result { + 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 for RecurrenceModifier { + async fn qread(_xml: &mut Reader) -> Result { unreachable!(); } } + +impl QRead for Expand { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for LimitRecurrenceSet { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for LimitFreebusySet { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead> for CalendarSelector { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for CompFilter { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for CompFilterRules { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for CompFilterMatch { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for PropFilter { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for PropFilterRules { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for PropFilterMatch { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for TimeOrText { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for TextMatch { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for ParamFilterMatch { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for TimeZone { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for Filter { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for TimeRange { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +impl QRead for CalProp { + async fn qread(_xml: &mut Reader) -> Result { + unreachable!(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + //use chrono::{FixedOffset, TimeZone}; + use crate::realization::Calendar; + //use quick_reader::NsReader; + + async fn deserialize>(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#" + + + + + Lisa's Events + + + +"#; + let got = deserialize::>(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) -> Result<(), QError> { + unreachable!(); + } +} + impl QWrite for CalendarDataPayload { async fn qwrite(&self, xml: &mut Writer) -> 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) -> Result<(), QError> { + unreachable!(); + } +} + impl QWrite for Comp { async fn qwrite(&self, xml: &mut Writer) -> 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 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 Writer { pub struct Reader { pub rdr: NsReader, cur: Event<'static>, + prev: Event<'static>, parents: Vec>, buf: Vec, } @@ -63,8 +64,9 @@ impl Reader { let mut buf: Vec = 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 Reader { async fn next(&mut self) -> Result, 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 Reader { } } + pub fn prev_attr(&self, attr: &str) -> Option { + 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, ParsingError> { //println!("close tag {:?}", self.parents.last());