diff options
author | Quentin Dufour <quentin@deuxfleurs.fr> | 2024-03-05 16:07:47 +0100 |
---|---|---|
committer | Quentin Dufour <quentin@deuxfleurs.fr> | 2024-03-05 16:07:47 +0100 |
commit | 8e5d8a8aaa7bde2357ca70542a88f744005d58ba (patch) | |
tree | 6c6276a296eb5e7b8ec83c7e361247276abc9c61 | |
parent | b7a990ecdb0346e12cfeb0de34565f51af61ec80 (diff) | |
download | aerogramme-8e5d8a8aaa7bde2357ca70542a88f744005d58ba.tar.gz aerogramme-8e5d8a8aaa7bde2357ca70542a88f744005d58ba.zip |
Refactor encoder+decoder WIP (compile)
-rw-r--r-- | src/dav/calencoder.rs | 2 | ||||
-rw-r--r-- | src/dav/caltypes.rs | 32 | ||||
-rw-r--r-- | src/dav/decoder.rs | 132 | ||||
-rw-r--r-- | src/dav/encoder.rs | 616 | ||||
-rw-r--r-- | src/dav/error.rs | 20 | ||||
-rw-r--r-- | src/dav/mod.rs | 3 | ||||
-rw-r--r-- | src/dav/realization.rs | 41 | ||||
-rw-r--r-- | src/dav/types.rs | 138 | ||||
-rw-r--r-- | src/dav/xml.rs | 128 |
9 files changed, 677 insertions, 435 deletions
diff --git a/src/dav/calencoder.rs b/src/dav/calencoder.rs index db7ece4..80b8656 100644 --- a/src/dav/calencoder.rs +++ b/src/dav/calencoder.rs @@ -1,3 +1,4 @@ +/* use super::encoder::{QuickWritable, Context}; use super::caltypes::*; use super::types::Extension; @@ -893,3 +894,4 @@ mod tests { assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n"); } } +*/ diff --git a/src/dav/caltypes.rs b/src/dav/caltypes.rs index 7131ec2..c8177e9 100644 --- a/src/dav/caltypes.rs +++ b/src/dav/caltypes.rs @@ -1,7 +1,8 @@ #![allow(dead_code)] +/* use chrono::{DateTime,Utc}; -use super::types as Dav; +use super::types as dav; //@FIXME ACL (rfc3744) is missing, required //@FIXME Versioning (rfc3253) is missing, required @@ -11,15 +12,6 @@ use super::types as Dav; // For reference, non-official extensions documented by SabreDAV: // https://github.com/apple/ccs-calendarserver/tree/master/doc/Extensions -pub struct CalExtension { - pub root: bool -} -impl Dav::Extension for CalExtension { - type Error = Violation; - type Property = Property; - type PropertyRequest = PropertyRequest; - type ResourceType = ResourceType; -} // ----- Root elements ----- @@ -35,7 +27,7 @@ impl Dav::Extension for CalExtension { /// instruction in Section 12.13.2 of [RFC2518]. /// /// <!ELEMENT mkcalendar (DAV:set)> -pub struct MkCalendar<E: Dav::Extension>(pub Dav::Set<E>); +pub struct MkCalendar<E: dav::Extension>(pub dav::Set<E>); /// If a response body for a successful request is included, it MUST @@ -51,7 +43,7 @@ pub struct MkCalendar<E: Dav::Extension>(pub Dav::Set<E>); /// Definition: /// /// <!ELEMENT mkcol-response (propstat+)> -pub struct MkCalendarResponse<T: Dav::Extension>(pub Vec<Dav::PropStat<T>>); +pub struct MkCalendarResponse<E: dav::Extension>(pub Vec<dav::PropStat<E>>); // --- (REPORT PART) --- @@ -68,8 +60,8 @@ pub struct MkCalendarResponse<T: Dav::Extension>(pub Vec<Dav::PropStat<T>>); /// <!ELEMENT calendar-query ((DAV:allprop | /// DAV:propname | /// DAV:prop)?, filter, timezone?)> -pub struct CalendarQuery<T: Dav::Extension> { - pub selector: Option<CalendarSelector<T>>, +pub struct CalendarQuery<E: dav::Extension> { + pub selector: Option<CalendarSelector<E>>, pub filter: Filter, pub timezone: Option<TimeZone>, } @@ -88,9 +80,9 @@ pub struct CalendarQuery<T: Dav::Extension> { /// <!ELEMENT calendar-multiget ((DAV:allprop | /// DAV:propname | /// DAV:prop)?, DAV:href+)> -pub struct CalendarMultiget<T: Dav::Extension> { - pub selector: Option<CalendarSelector<T>>, - pub href: Vec<Dav::Href>, +pub struct CalendarMultiget<E: dav::Extension> { + pub selector: Option<CalendarSelector<E>>, + pub href: Vec<dav::Href>, } /// Name: free-busy-query @@ -1056,10 +1048,10 @@ pub struct LimitRecurrenceSet(pub DateTime<Utc>, pub DateTime<Utc>); pub struct LimitFreebusySet(pub DateTime<Utc>, pub DateTime<Utc>); /// Used by CalendarQuery & CalendarMultiget -pub enum CalendarSelector<T: Dav::Extension> { +pub enum CalendarSelector<E: dav::Extension> { AllProp, PropName, - Prop(Dav::PropName<T>), + Prop(dav::PropName<E>), } /// Name: comp-filter @@ -1402,4 +1394,4 @@ impl Collation { Self::Unknown(c) => c.as_str(), } } -} +}*/ diff --git a/src/dav/decoder.rs b/src/dav/decoder.rs index 719ea8c..1756464 100644 --- a/src/dav/decoder.rs +++ b/src/dav/decoder.rs @@ -8,33 +8,32 @@ use quick_xml::reader::NsReader; use tokio::io::AsyncBufRead; use super::types::*; +use super::error::*; -#[derive(Debug)] -pub enum ParsingError { - NamespacePrefixAlreadyUsed, - WrongToken, - TagNotFound, - QuickXml(quick_xml::Error), - Eof +/* +// --- Traits ---- + +trait Reader = AsyncBufRead+Unpin+'static; + +trait Decodable: Extension { + async fn decode_propreq(xml: &mut PeekRead<impl Reader>) -> Result<Option<Self::PropertyRequest>, ParsingError>; } -impl From<AttrError> for ParsingError { - fn from(value: AttrError) -> Self { - Self::QuickXml(value.into()) +impl Decodable for NoExtension { + async fn decode_propreq(xml: &mut PeekRead<impl Reader>) -> Result<Option<Disabled>, ParsingError> { + Ok(None) } } -impl From<quick_xml::Error> for ParsingError { - fn from(value: quick_xml::Error) -> Self { - Self::QuickXml(value) - } + +pub trait QReadable<T: Reader>: Sized { + async fn read(xml: &mut PeekRead<T>) -> Result<Self, ParsingError>; } +// --- Peek read with namespaces + const DAV_URN: &[u8] = b"DAV:"; const CALDAV_URN: &[u8] = b"urn:ietf:params:xml:ns:caldav"; const CARDDAV_URN: &[u8] = b"urn:ietf:params:xml:ns:carddav"; //const XML_URN: &[u8] = b"xml"; - -trait Reader = AsyncBufRead+Unpin+'static; - pub struct PeekRead<T: Reader> { evt: Event<'static>, rdr: NsReader<T>, @@ -117,11 +116,9 @@ impl<T: Reader> PeekRead<T> { } } -pub trait QReadable<T: Reader>: Sized { - async fn read(xml: &mut PeekRead<T>) -> Result<Self, ParsingError>; -} +// ----- Decode ---- -impl<E: Extension, T: Reader> QReadable<T> for PropFind<E> { +impl<E: Decodable, T: Reader> QReadable<T> for PropFind<E> { async fn read(xml: &mut PeekRead<T>) -> Result<PropFind<E>, ParsingError> { // Find propfind xml.tag_start(DAV_URN, "propfind").await?; @@ -161,13 +158,13 @@ impl<E: Extension, T: Reader> QReadable<T> for PropFind<E> { } -impl<E: Extension, T: Reader> QReadable<T> for Include<E> { +impl<E: Decodable, T: Reader> QReadable<T> for Include<E> { async fn read(xml: &mut PeekRead<T>) -> Result<Include<E>, ParsingError> { xml.tag_start(DAV_URN, "include").await?; let mut acc: Vec<PropertyRequest<E>> = Vec::new(); loop { match xml.peek() { - Event::Start(_) => acc.push(PropertyRequest::read(xml).await?), + Event::Start(_) | Event::Empty(_) => acc.push(PropertyRequest::read(xml).await?), Event::End(_) if xml.is_tag(DAV_URN, "include") => break, _ => { xml.skip().await?; }, } @@ -177,13 +174,13 @@ impl<E: Extension, T: Reader> QReadable<T> for Include<E> { } } -impl<E: Extension, T: Reader> QReadable<T> for PropName<E> { +impl<E: Decodable, T: Reader> QReadable<T> for PropName<E> { async fn read(xml: &mut PeekRead<T>) -> Result<PropName<E>, ParsingError> { xml.tag_start(DAV_URN, "prop").await?; let mut acc: Vec<PropertyRequest<E>> = Vec::new(); loop { match xml.peek() { - Event::Start(_) => acc.push(PropertyRequest::read(xml).await?), + Event::Start(_) | Event::Empty(_) => acc.push(PropertyRequest::read(xml).await?), Event::End(_) if xml.is_tag(DAV_URN, "prop") => break, _ => { xml.skip().await?; }, } @@ -193,12 +190,52 @@ impl<E: Extension, T: Reader> QReadable<T> for PropName<E> { } } -impl<E: Extension, T: Reader> QReadable<T> for PropertyRequest<E> { +impl<E: Decodable, T: Reader> QReadable<T> for PropertyRequest<E> { async fn read(xml: &mut PeekRead<T>) -> Result<PropertyRequest<E>, ParsingError> { - /*match xml.peek() { - - }*/ - unimplemented!(); + loop { + let (need_close, bs) = match xml.peek() { + Event::Start(b) => (true, b), + Event::Empty(b) => (false, b), + _ => { + xml.skip().await?; + continue + }, + }; + + let mut maybe_res = None; + + // Option 1: a pure DAV property + let (ns, loc) = xml.rdr.resolve_element(bs.name()); + if matches!(ns, Bound(Namespace(ns)) if ns == DAV_URN) { + maybe_res = match loc.into_inner() { + b"creationdate" => Some(PropertyRequest::CreationDate), + b"displayname" => Some(PropertyRequest::DisplayName), + b"getcontentlanguage" => Some(PropertyRequest::GetContentLanguage), + b"getcontentlength" => Some(PropertyRequest::GetContentLength), + b"getetag" => Some(PropertyRequest::GetEtag), + b"getlastmodified" => Some(PropertyRequest::GetLastModified), + b"lockdiscovery" => Some(PropertyRequest::LockDiscovery), + b"resourcetype" => Some(PropertyRequest::ResourceType), + b"supportedlock" => Some(PropertyRequest::SupportedLock), + _ => None, + }; + } + + // Option 2: an extension property + if maybe_res.is_none() { + maybe_res = E::decode_propreq(xml).await?.map(PropertyRequest::Extension); + } + + // In any cases, we must close the opened tag + if need_close { + xml.skip().await?; + } + + // Return if something is found - otherwise loop + if let Some(res) = maybe_res { + return Ok(res) + } + } } } @@ -207,7 +244,7 @@ mod tests { use super::*; #[tokio::test] - async fn basic_propfind() { + async fn basic_propfind_propname() { let src = r#"<?xml version="1.0" encoding="utf-8" ?> <rando/> <garbage><old/></garbage> @@ -220,4 +257,37 @@ mod tests { let got = PropFind::<NoExtension>::read(&mut rdr).await.unwrap(); assert!(matches!(got, PropFind::PropName)); } +/* + #[tokio::test] + async fn basic_propfind_prop() { + let src = r#"<?xml version="1.0" encoding="utf-8" ?> +<rando/> +<garbage><old/></garbage> +<D:propfind xmlns:D="DAV:"> + <D:prop> + <displayname/> + <getcontentlength/> + <getcontenttype/> + <getetag/> + <getlastmodified/> + <resourcetype/> + <supportedlock/> + </D:prop> +</D:propfind> +"#; + + let mut rdr = PeekRead::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); + let got = PropFind::<NoExtension>::read(&mut rdr).await.unwrap(); + assert_eq!(got, PropFind::Prop(PropName(vec![ + PropertyRequest::DisplayName, + PropertyRequest::GetContentLength, + PropertyRequest::GetContentType, + PropertyRequest::GetEtag, + PropertyRequest::GetLastModified, + PropertyRequest::ResourceType, + PropertyRequest::SupportedLock, + ]))); + } + */ } +*/ diff --git a/src/dav/encoder.rs b/src/dav/encoder.rs index 7778d61..745c396 100644 --- a/src/dav/encoder.rs +++ b/src/dav/encoder.rs @@ -2,390 +2,339 @@ use std::io::Cursor; use quick_xml::Error as QError; use quick_xml::events::{Event, BytesEnd, BytesStart, BytesText}; -use quick_xml::writer::{ElementWriter, Writer}; +use quick_xml::writer::ElementWriter; use quick_xml::name::PrefixDeclaration; use tokio::io::AsyncWrite; use super::types::*; +use super::xml::{Writer,QWrite,IWrite}; -//-------------- TRAITS ---------------------- - -/// Basic encode trait to make a type encodable -pub trait QuickWritable<C: Context> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError>; -} - -/// Encoding context -pub trait Context: Extension { - fn child(&self) -> Self; - fn create_dav_element(&self, name: &str) -> BytesStart; - async fn hook_error(&self, err: &Self::Error, xml: &mut Writer<impl AsyncWrite+Unpin>) -> Result<(), QError>; - async fn hook_property(&self, prop: &Self::Property, xml: &mut Writer<impl AsyncWrite+Unpin>) -> Result<(), QError>; - async fn hook_propertyrequest(&self, prop: &Self::PropertyRequest, xml: &mut Writer<impl AsyncWrite+Unpin>) -> Result<(), QError>; - async fn hook_resourcetype(&self, prop: &Self::ResourceType, xml: &mut Writer<impl AsyncWrite+Unpin>) -> Result<(), QError>; -} - -/// -------------- NoExtension Encoding Context -impl Context for NoExtension { - fn child(&self) -> Self { - Self { root: false } - } - fn create_dav_element(&self, name: &str) -> BytesStart { - let mut start = BytesStart::new(format!("D:{}", name)); - if self.root { - start.push_attribute(("xmlns:D", "DAV:")); - } - start - } - async fn hook_error(&self, _err: &Disabled, _xml: &mut Writer<impl AsyncWrite+Unpin>) -> Result<(), QError> { - unreachable!(); - } - async fn hook_property(&self, _prop: &Disabled, _xml: &mut Writer<impl AsyncWrite+Unpin>) -> Result<(), QError> { - unreachable!(); - } - async fn hook_propertyrequest(&self, _prop: &Disabled, _xml: &mut Writer<impl AsyncWrite+Unpin>) -> Result<(), QError> { - unreachable!(); - } - async fn hook_resourcetype(&self, _restype: &Disabled, _xml: &mut Writer<impl AsyncWrite+Unpin>) -> Result<(), QError> { - unreachable!(); - } -} - - -//--------------------- ENCODING -------------------- - // --- XML ROOTS /// PROPFIND REQUEST -impl<C: Context> QuickWritable<C> for PropFind<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("propfind"); +impl<E: Extension> QWrite for PropFind<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("propfind"); let end = start.to_end(); - let ctx = ctx.child(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; match self { - Self::PropName => xml.write_event_async(Event::Empty(ctx.create_dav_element("propname"))).await?, + Self::PropName => { + let empty_propname = xml.create_dav_element("propname"); + xml.q.write_event_async(Event::Empty(empty_propname)).await? + }, Self::AllProp(maybe_include) => { - xml.write_event_async(Event::Empty(ctx.create_dav_element("allprop"))).await?; + let empty_allprop = xml.create_dav_element("allprop"); + xml.q.write_event_async(Event::Empty(empty_allprop)).await?; if let Some(include) = maybe_include { - include.write(xml, ctx.child()).await?; + include.qwrite(xml).await?; } }, - Self::Prop(propname) => propname.write(xml, ctx.child()).await?, + Self::Prop(propname) => propname.qwrite(xml).await?, } - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } /// PROPPATCH REQUEST -impl<C: Context> QuickWritable<C> for PropertyUpdate<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("propertyupdate"); +impl<E: Extension> QWrite for PropertyUpdate<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("propertyupdate"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for update in self.0.iter() { - update.write(xml, ctx.child()).await?; + update.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } /// PROPFIND RESPONSE, PROPPATCH RESPONSE, COPY RESPONSE, MOVE RESPONSE /// DELETE RESPONSE, -impl<C: Context> QuickWritable<C> for Multistatus<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("multistatus"); +impl<E: Extension> QWrite for Multistatus<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("multistatus"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for response in self.responses.iter() { - response.write(xml, ctx.child()).await?; + response.qwrite(xml).await?; } if let Some(description) = &self.responsedescription { - description.write(xml, ctx.child()).await?; + description.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::End(end)).await?; Ok(()) } } /// LOCK REQUEST -impl<C: Context> QuickWritable<C> for LockInfo { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("lockinfo"); +impl QWrite for LockInfo { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("lockinfo"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - self.lockscope.write(xml, ctx.child()).await?; - self.locktype.write(xml, ctx.child()).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.lockscope.qwrite(xml).await?; + self.locktype.qwrite(xml).await?; if let Some(owner) = &self.owner { - owner.write(xml, ctx.child()).await?; + owner.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } /// SOME LOCK RESPONSES -impl<C: Context> QuickWritable<C> for PropValue<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("prop"); +impl<E: Extension> QWrite for PropValue<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("prop"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for propval in &self.0 { - propval.write(xml, ctx.child()).await?; + propval.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } // --- XML inner elements -impl<C: Context> QuickWritable<C> for PropertyUpdateItem<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { +impl<E: Extension> QWrite for PropertyUpdateItem<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { match self { - Self::Set(set) => set.write(xml, ctx).await, - Self::Remove(rm) => rm.write(xml, ctx).await, + Self::Set(set) => set.qwrite(xml).await, + Self::Remove(rm) => rm.qwrite(xml).await, } } } -impl<C: Context> QuickWritable<C> for Set<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("set"); +impl<E: Extension> QWrite for Set<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("set"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - self.0.write(xml, ctx.child()).await?; - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.0.qwrite(xml).await?; + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for Remove<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("remove"); +impl<E: Extension> QWrite for Remove<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("remove"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - self.0.write(xml, ctx.child()).await?; - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.0.qwrite(xml).await?; + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for AnyProp<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { +impl<E: Extension> QWrite for AnyProp<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { match self { - Self::Name(propname) => propname.write(xml, ctx).await, - Self::Value(propval) => propval.write(xml, ctx).await, + Self::Name(propname) => propname.qwrite(xml).await, + Self::Value(propval) => propval.qwrite(xml).await, } } } -impl<C: Context> QuickWritable<C> for PropName<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("prop"); +impl<E: Extension> QWrite for PropName<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("prop"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for propname in &self.0 { - propname.write(xml, ctx.child()).await?; + propname.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for Href { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("href"); +impl QWrite for Href { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("href"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - xml.write_event_async(Event::Text(BytesText::new(&self.0))).await?; - xml.write_event_async(Event::End(end)).await?; - - Ok(()) + xml.q.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Text(BytesText::new(&self.0))).await?; + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for Response<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("response"); +impl<E: Extension> QWrite for Response<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("response"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - self.href.write(xml, ctx.child()).await?; - self.status_or_propstat.write(xml, ctx.child()).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.href.qwrite(xml).await?; + self.status_or_propstat.qwrite(xml).await?; if let Some(error) = &self.error { - error.write(xml, ctx.child()).await?; + error.qwrite(xml).await?; } if let Some(responsedescription) = &self.responsedescription { - responsedescription.write(xml, ctx.child()).await?; + responsedescription.qwrite(xml).await?; } if let Some(location) = &self.location { - location.write(xml, ctx.child()).await?; + location.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await?; - - Ok(()) + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for StatusOrPropstat<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { +impl<E: Extension> QWrite for StatusOrPropstat<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { match self { - Self::Status(status) => status.write(xml, ctx.child()).await, + Self::Status(status) => status.qwrite(xml).await, Self::PropStat(propstat_list) => { for propstat in propstat_list.iter() { - propstat.write(xml, ctx.child()).await?; + propstat.qwrite(xml).await?; } - Ok(()) } } } } -impl<C: Context> QuickWritable<C> for Status { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("status"); +impl QWrite for Status { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("status"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; let txt = format!("HTTP/1.1 {} {}", self.0.as_str(), self.0.canonical_reason().unwrap_or("No reason")); - xml.write_event_async(Event::Text(BytesText::new(&txt))).await?; + xml.q.write_event_async(Event::Text(BytesText::new(&txt))).await?; - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::End(end)).await?; Ok(()) } } -impl<C: Context> QuickWritable<C> for ResponseDescription { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("responsedescription"); +impl QWrite for ResponseDescription { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("responsedescription"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - xml.write_event_async(Event::Text(BytesText::new(&self.0))).await?; - xml.write_event_async(Event::End(end)).await?; - - Ok(()) + xml.q.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Text(BytesText::new(&self.0))).await?; + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for Location { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("location"); +impl QWrite for Location { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("location"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - self.0.write(xml, ctx.child()).await?; - xml.write_event_async(Event::End(end)).await?; - - Ok(()) + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.0.qwrite(xml).await?; + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for PropStat<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("propstat"); +impl<E: Extension> QWrite for PropStat<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("propstat"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - self.prop.write(xml, ctx.child()).await?; - self.status.write(xml, ctx.child()).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.prop.qwrite(xml).await?; + self.status.qwrite(xml).await?; if let Some(error) = &self.error { - error.write(xml, ctx.child()).await?; + error.qwrite(xml).await?; } if let Some(description) = &self.responsedescription { - description.write(xml, ctx.child()).await?; + description.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::End(end)).await?; Ok(()) } } -impl<C: Context> QuickWritable<C> for Property<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { +impl<E: Extension> QWrite for Property<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { use Property::*; match self { CreationDate(date) => { // <D:creationdate>1997-12-01T17:42:21-08:00</D:creationdate> - let start = ctx.create_dav_element("creationdate"); + let start = xml.create_dav_element("creationdate"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - xml.write_event_async(Event::Text(BytesText::new(&date.to_rfc3339()))).await?; - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Text(BytesText::new(&date.to_rfc3339()))).await?; + xml.q.write_event_async(Event::End(end)).await?; }, DisplayName(name) => { // <D:displayname>Example collection</D:displayname> - let start = ctx.create_dav_element("displayname"); + let start = xml.create_dav_element("displayname"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - xml.write_event_async(Event::Text(BytesText::new(name))).await?; - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Text(BytesText::new(name))).await?; + xml.q.write_event_async(Event::End(end)).await?; }, GetContentLanguage(lang) => { - let start = ctx.create_dav_element("getcontentlanguage"); + let start = xml.create_dav_element("getcontentlanguage"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - xml.write_event_async(Event::Text(BytesText::new(lang))).await?; - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Text(BytesText::new(lang))).await?; + xml.q.write_event_async(Event::End(end)).await?; }, GetContentLength(len) => { // <D:getcontentlength>4525</D:getcontentlength> - let start = ctx.create_dav_element("getcontentlength"); + let start = xml.create_dav_element("getcontentlength"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - xml.write_event_async(Event::Text(BytesText::new(&len.to_string()))).await?; - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Text(BytesText::new(&len.to_string()))).await?; + xml.q.write_event_async(Event::End(end)).await?; }, GetContentType(ct) => { // <D:getcontenttype>text/html</D:getcontenttype> - let start = ctx.create_dav_element("getcontenttype"); + let start = xml.create_dav_element("getcontenttype"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - xml.write_event_async(Event::Text(BytesText::new(&ct))).await?; - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Text(BytesText::new(&ct))).await?; + xml.q.write_event_async(Event::End(end)).await?; }, GetEtag(et) => { // <D:getetag>"zzyzx"</D:getetag> - let start = ctx.create_dav_element("getetag"); + let start = xml.create_dav_element("getetag"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - xml.write_event_async(Event::Text(BytesText::new(et))).await?; - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Text(BytesText::new(et))).await?; + xml.q.write_event_async(Event::End(end)).await?; }, GetLastModified(date) => { // <D:getlastmodified>Mon, 12 Jan 1998 09:25:56 GMT</D:getlastmodified> - let start = ctx.create_dav_element("getlastmodified"); + let start = xml.create_dav_element("getlastmodified"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - xml.write_event_async(Event::Text(BytesText::new(&date.to_rfc2822()))).await?; - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Text(BytesText::new(&date.to_rfc2822()))).await?; + xml.q.write_event_async(Event::End(end)).await?; }, LockDiscovery(many_locks) => { // <D:lockdiscovery><D:activelock> ... </D:activelock></D:lockdiscovery> - let start = ctx.create_dav_element("lockdiscovery"); + let start = xml.create_dav_element("lockdiscovery"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for lock in many_locks.iter() { - lock.write(xml, ctx.child()).await?; + lock.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::End(end)).await?; }, ResourceType(many_types) => { // <D:resourcetype><D:collection/></D:resourcetype> @@ -397,16 +346,16 @@ impl<C: Context> QuickWritable<C> for Property<C> { // <f:search-results xmlns:f="http://www.example.com/ns"/> // </x:resourcetype> - let start = ctx.create_dav_element("resourcetype"); + let start = xml.create_dav_element("resourcetype"); if many_types.is_empty() { - xml.write_event_async(Event::Empty(start)).await?; + xml.q.write_event_async(Event::Empty(start)).await?; } else { let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for restype in many_types.iter() { - restype.write(xml, ctx.child()).await?; + restype.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::End(end)).await?; } }, SupportedLock(many_entries) => { @@ -414,52 +363,56 @@ impl<C: Context> QuickWritable<C> for Property<C> { // <D:supportedlock> <D:lockentry> ... </D:lockentry> </D:supportedlock> - let start = ctx.create_dav_element("supportedlock"); + let start = xml.create_dav_element("supportedlock"); if many_entries.is_empty() { - xml.write_event_async(Event::Empty(start)).await?; + xml.q.write_event_async(Event::Empty(start)).await?; } else { let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for entry in many_entries.iter() { - entry.write(xml, ctx.child()).await?; + entry.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await?; + xml.q.write_event_async(Event::End(end)).await?; } }, - Extension(inner) => { - ctx.hook_property(inner, xml).await?; - }, + Extension(inner) => inner.qwrite(xml).await?, }; Ok(()) } } -impl<C: Context> QuickWritable<C> for ResourceType<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { +impl<E: Extension> QWrite for ResourceType<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { match self { - Self::Collection => xml.write_event_async(Event::Empty(ctx.create_dav_element("collection"))).await, - Self::Extension(inner) => ctx.hook_resourcetype(inner, xml).await, + Self::Collection => { + let empty_collection = xml.create_dav_element("collection"); + xml.q.write_event_async(Event::Empty(empty_collection)).await + }, + Self::Extension(inner) => inner.qwrite(xml).await, } } } -impl<C: Context> QuickWritable<C> for Include<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("include"); +impl<E: Extension> QWrite for Include<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("include"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for prop in self.0.iter() { - prop.write(xml, ctx.child()).await?; + prop.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for PropertyRequest<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { +impl<E: Extension> QWrite for PropertyRequest<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { use PropertyRequest::*; - let mut atom = async |c| xml.write_event_async(Event::Empty(ctx.create_dav_element(c))).await; + let mut atom = async |c| { + let empty_tag = xml.create_dav_element(c); + xml.q.write_event_async(Event::Empty(empty_tag)).await + }; match self { CreationDate => atom("creationdate").await, @@ -472,13 +425,13 @@ impl<C: Context> QuickWritable<C> for PropertyRequest<C> { LockDiscovery => atom("lockdiscovery").await, ResourceType => atom("resourcetype").await, SupportedLock => atom("supportedlock").await, - Extension(inner) => ctx.hook_propertyrequest(inner, xml).await, + Extension(inner) => inner.qwrite(xml).await, } } } -impl<C: Context> QuickWritable<C> for ActiveLock { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { +impl QWrite for ActiveLock { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { // <D:activelock> // <D:locktype><D:write/></D:locktype> // <D:lockscope><D:exclusive/></D:lockscope> @@ -494,192 +447,193 @@ impl<C: Context> QuickWritable<C> for ActiveLock { // <D:href>http://example.com/workspace/webdav/proposal.doc</D:href> // </D:lockroot> // </D:activelock> - let start = ctx.create_dav_element("activelock"); + let start = xml.create_dav_element("activelock"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - self.locktype.write(xml, ctx.child()).await?; - self.lockscope.write(xml, ctx.child()).await?; - self.depth.write(xml, ctx.child()).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.locktype.qwrite(xml).await?; + self.lockscope.qwrite(xml).await?; + self.depth.qwrite(xml).await?; if let Some(owner) = &self.owner { - owner.write(xml, ctx.child()).await?; + owner.qwrite(xml).await?; } if let Some(timeout) = &self.timeout { - timeout.write(xml, ctx.child()).await?; + timeout.qwrite(xml).await?; } if let Some(locktoken) = &self.locktoken { - locktoken.write(xml, ctx.child()).await?; + locktoken.qwrite(xml).await?; } - self.lockroot.write(xml, ctx.child()).await?; - xml.write_event_async(Event::End(end)).await?; - - Ok(()) + self.lockroot.qwrite(xml).await?; + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for LockType { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("locktype"); +impl QWrite for LockType { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("locktype"); let end = start.to_end(); - let ctx = ctx.child(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; match self { - Self::Write => xml.write_event_async(Event::Empty(ctx.create_dav_element("write"))).await?, + Self::Write => { + let empty_write = xml.create_dav_element("write"); + xml.q.write_event_async(Event::Empty(empty_write)).await? + }, }; - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for LockScope { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("lockscope"); +impl QWrite for LockScope { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("lockscope"); let end = start.to_end(); - let ctx = ctx.child(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; match self { - Self::Exclusive => xml.write_event_async(Event::Empty(ctx.create_dav_element("exclusive"))).await?, - Self::Shared => xml.write_event_async(Event::Empty(ctx.create_dav_element("shared"))).await?, + Self::Exclusive => { + let empty_tag = xml.create_dav_element("exclusive"); + xml.q.write_event_async(Event::Empty(empty_tag)).await? + }, + Self::Shared => { + let empty_tag = xml.create_dav_element("shared"); + xml.q.write_event_async(Event::Empty(empty_tag)).await? + }, }; - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for Owner { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("owner"); +impl QWrite for Owner { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("owner"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; match self { - Self::Txt(txt) => xml.write_event_async(Event::Text(BytesText::new(&txt))).await?, - Self::Href(href) => href.write(xml, ctx.child()).await?, + Self::Txt(txt) => xml.q.write_event_async(Event::Text(BytesText::new(&txt))).await?, + Self::Href(href) => href.qwrite(xml).await?, } - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for Depth { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("depth"); +impl QWrite for Depth { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("depth"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; match self { - Self::Zero => xml.write_event_async(Event::Text(BytesText::new("0"))).await?, - Self::One => xml.write_event_async(Event::Text(BytesText::new("1"))).await?, - Self::Infinity => xml.write_event_async(Event::Text(BytesText::new("infinity"))).await?, + Self::Zero => xml.q.write_event_async(Event::Text(BytesText::new("0"))).await?, + Self::One => xml.q.write_event_async(Event::Text(BytesText::new("1"))).await?, + Self::Infinity => xml.q.write_event_async(Event::Text(BytesText::new("infinity"))).await?, }; - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for Timeout { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("timeout"); +impl QWrite for Timeout { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("timeout"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; match self { Self::Seconds(count) => { let txt = format!("Second-{}", count); - xml.write_event_async(Event::Text(BytesText::new(&txt))).await? + xml.q.write_event_async(Event::Text(BytesText::new(&txt))).await? }, - Self::Infinite => xml.write_event_async(Event::Text(BytesText::new("Infinite"))).await? + Self::Infinite => xml.q.write_event_async(Event::Text(BytesText::new("Infinite"))).await? }; - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for LockToken { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("locktoken"); +impl QWrite for LockToken { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("locktoken"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - self.0.write(xml, ctx.child()).await?; - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.0.qwrite(xml).await?; + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for LockRoot { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("lockroot"); +impl QWrite for LockRoot { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("lockroot"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - self.0.write(xml, ctx.child()).await?; - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.0.qwrite(xml).await?; + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for LockEntry { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("lockentry"); +impl QWrite for LockEntry { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("lockentry"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; - self.lockscope.write(xml, ctx.child()).await?; - self.locktype.write(xml, ctx.child()).await?; - xml.write_event_async(Event::End(end)).await + xml.q.write_event_async(Event::Start(start.clone())).await?; + self.lockscope.qwrite(xml).await?; + self.locktype.qwrite(xml).await?; + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for Error<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { - let start = ctx.create_dav_element("error"); +impl<E: Extension> QWrite for Error<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let start = xml.create_dav_element("error"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for violation in &self.0 { - violation.write(xml, ctx.child()).await?; + violation.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await?; - - Ok(()) + xml.q.write_event_async(Event::End(end)).await } } -impl<C: Context> QuickWritable<C> for Violation<C> { - async fn write(&self, xml: &mut Writer<impl AsyncWrite+Unpin>, ctx: C) -> Result<(), QError> { +impl<E: Extension> QWrite for Violation<E> { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { + let mut atom = async |c| { + let empty_tag = xml.create_dav_element(c); + xml.q.write_event_async(Event::Empty(empty_tag)).await + }; + match self { - Violation::LockTokenMatchesRequestUri => xml.write_event_async(Event::Empty(ctx.create_dav_element("lock-token-matches-request-uri"))).await?, - Violation::LockTokenSubmitted(hrefs) if hrefs.is_empty() => { - xml.write_event_async(Event::Empty(ctx.create_dav_element("lock-token-submitted"))).await? - }, + Violation::LockTokenMatchesRequestUri => atom("lock-token-matches-request-uri").await, + Violation::LockTokenSubmitted(hrefs) if hrefs.is_empty() => atom("lock-token-submitted").await, Violation::LockTokenSubmitted(hrefs) => { - let start = ctx.create_dav_element("lock-token-submitted"); + let start = xml.create_dav_element("lock-token-submitted"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for href in hrefs { - href.write(xml, ctx.child()).await?; + href.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await?; - }, - Violation::NoConflictingLock(hrefs) if hrefs.is_empty() => { - xml.write_event_async(Event::Empty(ctx.create_dav_element("no-conflicting-lock"))).await? + xml.q.write_event_async(Event::End(end)).await }, + Violation::NoConflictingLock(hrefs) if hrefs.is_empty() => atom("no-conflicting-lock").await, Violation::NoConflictingLock(hrefs) => { - let start = ctx.create_dav_element("no-conflicting-lock"); + let start = xml.create_dav_element("no-conflicting-lock"); let end = start.to_end(); - xml.write_event_async(Event::Start(start.clone())).await?; + xml.q.write_event_async(Event::Start(start.clone())).await?; for href in hrefs { - href.write(xml, ctx.child()).await?; + href.qwrite(xml).await?; } - xml.write_event_async(Event::End(end)).await?; - }, - Violation::NoExternalEntities => xml.write_event_async(Event::Empty(ctx.create_dav_element("no-external-entities"))).await?, - Violation::PreservedLiveProperties => xml.write_event_async(Event::Empty(ctx.create_dav_element("preserved-live-properties"))).await?, - Violation::PropfindFiniteDepth => xml.write_event_async(Event::Empty(ctx.create_dav_element("propfind-finite-depth"))).await?, - Violation::CannotModifyProtectedProperty => xml.write_event_async(Event::Empty(ctx.create_dav_element("cannot-modify-protected-property"))).await?, - Violation::Extension(inner) => { - ctx.hook_error(inner, xml).await?; + xml.q.write_event_async(Event::End(end)).await }, - }; - Ok(()) + Violation::NoExternalEntities => atom("no-external-entities").await, + Violation::PreservedLiveProperties => atom("preserved-live-properties").await, + Violation::PropfindFiniteDepth => atom("propfind-finite-depth").await, + Violation::CannotModifyProtectedProperty => atom("cannot-modify-protected-property").await, + Violation::Extension(inner) => inner.qwrite(xml).await, + } } } diff --git a/src/dav/error.rs b/src/dav/error.rs new file mode 100644 index 0000000..1db2895 --- /dev/null +++ b/src/dav/error.rs @@ -0,0 +1,20 @@ +use quick_xml::events::attributes::AttrError; + +#[derive(Debug)] +pub enum ParsingError { + NamespacePrefixAlreadyUsed, + WrongToken, + TagNotFound, + QuickXml(quick_xml::Error), + Eof +} +impl From<AttrError> for ParsingError { + fn from(value: AttrError) -> Self { + Self::QuickXml(value.into()) + } +} +impl From<quick_xml::Error> for ParsingError { + fn from(value: quick_xml::Error) -> Self { + Self::QuickXml(value) + } +} diff --git a/src/dav/mod.rs b/src/dav/mod.rs index 835544b..abc46e7 100644 --- a/src/dav/mod.rs +++ b/src/dav/mod.rs @@ -1,3 +1,5 @@ +mod error; +mod xml; mod types; mod caltypes; mod acltypes; @@ -5,6 +7,7 @@ mod versioningtypes; mod encoder; mod calencoder; mod decoder; +mod realization; use std::net::SocketAddr; diff --git a/src/dav/realization.rs b/src/dav/realization.rs new file mode 100644 index 0000000..22c9cfc --- /dev/null +++ b/src/dav/realization.rs @@ -0,0 +1,41 @@ +use super::types as dav; +use super::caltypes as cal; +use super::xml; +use super::error; + +#[derive(Debug, PartialEq)] +pub struct Disabled(()); +impl xml::QRead<Disabled> for Disabled { + async fn qread(&self, xml: &mut xml::Reader<impl xml::IRead>) -> Result<Option<Self>, error::ParsingError> { + unreachable!(); + } +} +impl xml::QWrite for Disabled { + async fn qwrite(&self, xml: &mut xml::Writer<impl xml::IWrite>) -> Result<(), quick_xml::Error> { + unreachable!(); + } +} + +/// The base WebDAV +/// +/// Any extension is kooh is disabled through an object we can't build +/// due to a private inner element. +pub struct Core {} +impl dav::Extension for Core { + type Error = Disabled; + type Property = Disabled; + type PropertyRequest = Disabled; + type ResourceType = Disabled; +} + +/* +// WebDAV with the base Calendar implementation (RFC4791) +pub struct CalendarMin {} +impl dav::Extension for CalendarMin +{ + type Error = cal::Violation; + type Property = cal::Property; + type PropertyRequest = cal::PropertyRequest; + type ResourceType = cal::ResourceType; +} +*/ diff --git a/src/dav/types.rs b/src/dav/types.rs index a1b1c7f..f2eae3a 100644 --- a/src/dav/types.rs +++ b/src/dav/types.rs @@ -1,28 +1,20 @@ #![allow(dead_code)] +use std::fmt::Debug; use chrono::{DateTime,FixedOffset}; +use super::xml; +use super::error; -/// Extension utilities -pub struct Disabled(()); +/// It's how we implement a DAV extension +/// (That's the dark magic part...) +pub trait ExtensionItem<T> = xml::QRead<T> + xml::QWrite + Debug + PartialEq; pub trait Extension { - type Error; - type Property; - type PropertyRequest; - type ResourceType; + type Error: ExtensionItem<Self::Error>; + type Property: ExtensionItem<Self::Property>; + type PropertyRequest: ExtensionItem<Self::PropertyRequest>; + type ResourceType: ExtensionItem<Self::ResourceType>; } -/// No extension -pub struct NoExtension { - pub root: bool -} -impl Extension for NoExtension { - type Error = Disabled; - type Property = Disabled; - type PropertyRequest = Disabled; - type ResourceType = Disabled; -} - - /// 14.1. activelock XML Element /// /// Name: activelock @@ -30,6 +22,7 @@ impl Extension for NoExtension { /// Purpose: Describes a lock on a resource. /// <!ELEMENT activelock (lockscope, locktype, depth, owner?, timeout?, /// locktoken?, lockroot)> +#[derive(Debug, PartialEq)] pub struct ActiveLock { pub lockscope: LockScope, pub locktype: LockType, @@ -50,6 +43,7 @@ pub struct ActiveLock { /// elements. /// /// <!ELEMENT collection EMPTY > +#[derive(Debug, PartialEq)] pub struct Collection{} /// 14.4 depth XML Element @@ -62,6 +56,7 @@ pub struct Collection{} /// Value: "0" | "1" | "infinity" /// /// <!ELEMENT depth (#PCDATA) > +#[derive(Debug, PartialEq)] pub enum Depth { Zero, One, @@ -84,8 +79,10 @@ pub enum Depth { /// postcondition code. Unrecognized elements MUST be ignored. /// /// <!ELEMENT error ANY > -pub struct Error<T: Extension>(pub Vec<Violation<T>>); -pub enum Violation<T: Extension> { +#[derive(Debug, PartialEq)] +pub struct Error<E: Extension>(pub Vec<Violation<E>>); +#[derive(Debug, PartialEq)] +pub enum Violation<E: Extension> { /// Name: lock-token-matches-request-uri /// /// Use with: 409 Conflict @@ -169,7 +166,7 @@ pub enum Violation<T: Extension> { CannotModifyProtectedProperty, /// Specific errors - Extension(T::Error), + Extension(E::Error), } /// 14.6. exclusive XML Element @@ -179,6 +176,7 @@ pub enum Violation<T: Extension> { /// Purpose: Specifies an exclusive lock. /// /// <!ELEMENT exclusive EMPTY > +#[derive(Debug, PartialEq)] pub struct Exclusive {} /// 14.7. href XML Element @@ -194,6 +192,7 @@ pub struct Exclusive {} /// Value: Simple-ref /// /// <!ELEMENT href (#PCDATA)> +#[derive(Debug, PartialEq)] pub struct Href(pub String); @@ -209,7 +208,8 @@ pub struct Href(pub String); /// standards. This element MUST NOT contain text or mixed content. /// /// <!ELEMENT include ANY > -pub struct Include<T: Extension>(pub Vec<PropertyRequest<T>>); +#[derive(Debug, PartialEq)] +pub struct Include<E: Extension>(pub Vec<PropertyRequest<E>>); /// 14.9. location XML Element /// @@ -225,6 +225,7 @@ pub struct Include<T: Extension>(pub Vec<PropertyRequest<T>>); /// that would be used in a Location header. /// /// <!ELEMENT location (href)> +#[derive(Debug, PartialEq)] pub struct Location(pub Href); /// 14.10. lockentry XML Element @@ -235,6 +236,7 @@ pub struct Location(pub Href); /// resource. /// /// <!ELEMENT lockentry (lockscope, locktype) > +#[derive(Debug, PartialEq)] pub struct LockEntry { pub lockscope: LockScope, pub locktype: LockType, @@ -248,6 +250,7 @@ pub struct LockEntry { /// specify the type of lock the client wishes to have created. /// /// <!ELEMENT lockinfo (lockscope, locktype, owner?) > +#[derive(Debug, PartialEq)] pub struct LockInfo { pub lockscope: LockScope, pub locktype: LockType, @@ -266,6 +269,7 @@ pub struct LockInfo { /// values and the response to LOCK requests. /// /// <!ELEMENT lockroot (href) > +#[derive(Debug, PartialEq)] pub struct LockRoot(pub Href); /// 14.13. lockscope XML Element @@ -275,6 +279,7 @@ pub struct LockRoot(pub Href); /// Purpose: Specifies whether a lock is an exclusive lock, or a shared /// lock. /// <!ELEMENT lockscope (exclusive | shared) > +#[derive(Debug, PartialEq)] pub enum LockScope { Exclusive, Shared @@ -290,6 +295,7 @@ pub enum LockScope { /// refers to the lock. /// /// <!ELEMENT locktoken (href) > +#[derive(Debug, PartialEq)] pub struct LockToken(pub Href); /// 14.15. locktype XML Element @@ -300,6 +306,7 @@ pub struct LockToken(pub Href); /// specification only defines one lock type, the write lock. /// /// <!ELEMENT locktype (write) > +#[derive(Debug, PartialEq)] pub enum LockType { /// 14.30. write XML Element /// @@ -325,8 +332,9 @@ pub enum LockType { /// response descriptions contained within the responses. /// /// <!ELEMENT multistatus (response*, responsedescription?) > -pub struct Multistatus<T: Extension> { - pub responses: Vec<Response<T>>, +#[derive(Debug, PartialEq)] +pub struct Multistatus<E: Extension> { + pub responses: Vec<Response<E>>, pub responsedescription: Option<ResponseDescription>, } @@ -354,6 +362,7 @@ pub struct Multistatus<T: Extension> { /// /// <!ELEMENT owner ANY > //@FIXME might need support for an extension +#[derive(Debug, PartialEq)] pub enum Owner { Txt(String), Href(Href), @@ -373,12 +382,17 @@ pub enum Owner { /// text or mixed content. /// /// <!ELEMENT prop ANY > -pub enum AnyProp<T: Extension> { - Name(PropName<T>), - Value(PropValue<T>), +#[derive(Debug, PartialEq)] +pub enum AnyProp<E: Extension> { + Name(PropName<E>), + Value(PropValue<E>), } -pub struct PropName<T: Extension>(pub Vec<PropertyRequest<T>>); -pub struct PropValue<T: Extension>(pub Vec<Property<T>>); + +#[derive(Debug, PartialEq)] +pub struct PropName<E: Extension>(pub Vec<PropertyRequest<E>>); + +#[derive(Debug, PartialEq)] +pub struct PropValue<E: Extension>(pub Vec<Property<E>>); /// 14.19. propertyupdate XML Element /// @@ -390,10 +404,13 @@ pub struct PropValue<T: Extension>(pub Vec<Property<T>>); /// required to modify the properties on the resource. /// /// <!ELEMENT propertyupdate (remove | set)+ > -pub struct PropertyUpdate<T: Extension>(pub Vec<PropertyUpdateItem<T>>); -pub enum PropertyUpdateItem<T: Extension> { - Remove(Remove<T>), - Set(Set<T>), +#[derive(Debug, PartialEq)] +pub struct PropertyUpdate<E: Extension>(pub Vec<PropertyUpdateItem<E>>); + +#[derive(Debug, PartialEq)] +pub enum PropertyUpdateItem<E: Extension> { + Remove(Remove<E>), + Set(Set<E>), } /// 14.2 allprop XML Element @@ -430,10 +447,11 @@ pub enum PropertyUpdateItem<T: Extension> { /// values. /// /// <!ELEMENT propfind ( propname | (allprop, include?) | prop ) > -pub enum PropFind<T: Extension> { +#[derive(Debug, PartialEq)] +pub enum PropFind<E: Extension> { PropName, - AllProp(Option<Include<T>>), - Prop(PropName<T>), + AllProp(Option<Include<E>>), + Prop(PropName<E>), } /// 14.22 propstat XML Element @@ -451,10 +469,11 @@ pub enum PropFind<T: Extension> { /// the properties named in 'prop'. /// /// <!ELEMENT propstat (prop, status, error?, responsedescription?) > -pub struct PropStat<T: Extension> { - pub prop: AnyProp<T>, +#[derive(Debug, PartialEq)] +pub struct PropStat<E: Extension> { + pub prop: AnyProp<E>, pub status: Status, - pub error: Option<Error<T>>, + pub error: Option<Error<E>>, pub responsedescription: Option<ResponseDescription>, } @@ -471,7 +490,8 @@ pub struct PropStat<T: Extension> { /// the names of properties to be removed are required. /// /// <!ELEMENT remove (prop) > -pub struct Remove<T: Extension>(pub PropName<T>); +#[derive(Debug, PartialEq)] +pub struct Remove<E: Extension>(pub PropName<E>); /// 14.24. response XML Element /// @@ -495,14 +515,17 @@ pub struct Remove<T: Extension>(pub PropName<T>); /// /// <!ELEMENT response (href, ((href*, status)|(propstat+)), /// error?, responsedescription? , location?) > -pub enum StatusOrPropstat<T: Extension> { +#[derive(Debug, PartialEq)] +pub enum StatusOrPropstat<E: Extension> { Status(Status), - PropStat(Vec<PropStat<T>>), + PropStat(Vec<PropStat<E>>), } -pub struct Response<T: Extension> { + +#[derive(Debug, PartialEq)] +pub struct Response<E: Extension> { pub href: Href, // It's wrong according to the spec, but I don't understand why there is an href* - pub status_or_propstat: StatusOrPropstat<T>, - pub error: Option<Error<T>>, + pub status_or_propstat: StatusOrPropstat<E>, + pub error: Option<Error<E>>, pub responsedescription: Option<ResponseDescription>, pub location: Option<Location>, } @@ -518,6 +541,7 @@ pub struct Response<T: Extension> { /// user. /// /// <!ELEMENT responsedescription (#PCDATA) > +#[derive(Debug, PartialEq)] pub struct ResponseDescription(pub String); /// 14.26. set XML Element @@ -536,7 +560,8 @@ pub struct ResponseDescription(pub String); /// property, and MUST be subsequently retrievable using PROPFIND. /// /// <!ELEMENT set (prop) > -pub struct Set<T: Extension>(pub PropValue<T>); +#[derive(Debug, PartialEq)] +pub struct Set<E: Extension>(pub PropValue<E>); /// 14.27. shared XML Element /// @@ -546,6 +571,7 @@ pub struct Set<T: Extension>(pub PropValue<T>); /// /// /// <!ELEMENT shared EMPTY > +#[derive(Debug, PartialEq)] pub struct Shared {} @@ -559,6 +585,7 @@ pub struct Shared {} /// /// <!ELEMENT status (#PCDATA) > //@FIXME: Better typing is possible with an enum for example +#[derive(Debug, PartialEq)] pub struct Status(pub http::status::StatusCode); /// 14.29. timeout XML Element @@ -586,6 +613,7 @@ pub struct Status(pub http::status::StatusCode); /// elapse between granting of the lock at the server, and the automatic /// removal of the lock. The timeout value for TimeType "Second" MUST /// NOT be greater than 2^32-1. +#[derive(Debug, PartialEq)] pub enum Timeout { Seconds(u32), Infinite, @@ -619,7 +647,8 @@ pub enum Timeout { /// the header value could include LWS as defined in [RFC2616], Section /// 4.2. Server implementors SHOULD strip LWS from these values before /// using as WebDAV property values. -pub enum PropertyRequest<T: Extension> { +#[derive(Debug, PartialEq)] +pub enum PropertyRequest<E: Extension> { CreationDate, DisplayName, GetContentLanguage, @@ -630,9 +659,11 @@ pub enum PropertyRequest<T: Extension> { LockDiscovery, ResourceType, SupportedLock, - Extension(T::PropertyRequest), + Extension(E::PropertyRequest), } -pub enum Property<T: Extension> { + +#[derive(Debug, PartialEq)] +pub enum Property<E: Extension> { /// 15.1. creationdate Property /// /// Name: creationdate @@ -883,7 +914,7 @@ pub enum Property<T: Extension> { /// <x:collection/> /// <f:search-results xmlns:f="http://www.example.com/ns"/> /// </x:resourcetype> - ResourceType(Vec<ResourceType<T>>), + ResourceType(Vec<ResourceType<E>>), /// 15.10. supportedlock Property /// @@ -911,10 +942,11 @@ pub enum Property<T: Extension> { SupportedLock(Vec<LockEntry>), /// Any extension - Extension(T::Property), + Extension(E::Property), } -pub enum ResourceType<T: Extension> { +#[derive(Debug, PartialEq)] +pub enum ResourceType<E: Extension> { Collection, - Extension(T::ResourceType), + Extension(E::ResourceType), } diff --git a/src/dav/xml.rs b/src/dav/xml.rs new file mode 100644 index 0000000..777f99e --- /dev/null +++ b/src/dav/xml.rs @@ -0,0 +1,128 @@ +use std::collections::HashMap; +use tokio::io::{AsyncWrite, AsyncBufRead}; +use quick_xml::events::{Event, BytesEnd, BytesStart, BytesText}; +use quick_xml::name::{Namespace, QName, PrefixDeclaration, ResolveResult, ResolveResult::*}; +use quick_xml::reader::NsReader; + +use super::error::ParsingError; + +// Async traits +pub trait IWrite = AsyncWrite + Unpin; +pub trait IRead = AsyncBufRead + Unpin + 'static; + +// Serialization/Deserialization traits +pub trait QWrite { + async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), quick_xml::Error>; +} +pub trait QRead<T> { + async fn qread(&self, xml: &mut Reader<impl IRead>) -> Result<Option<T>, ParsingError>; +} + +/// Transform a Rust object into an XML stream of characters +pub struct Writer<T: IWrite> { + pub q: quick_xml::writer::Writer<T>, + root: bool, +} +impl<T: IWrite> Writer<T> { + pub fn create_dav_element(&mut self, name: &str) -> BytesStart<'static> { + self.create_ns_element("D", name) + } + pub fn create_cal_element(&mut self, name: &str) -> BytesStart<'static> { + self.create_ns_element("C", name) + } + + fn create_ns_element(&mut self, ns: &str, name: &str) -> BytesStart<'static> { + let mut start = BytesStart::new(format!("{}:{}", ns, name)); + //@FIXME not what we want + if self.root { + start.push_attribute(("xmlns:D", "DAV:")); + start.push_attribute(("xmlns:C", "urn:ietf:params:xml:ns:caldav")); + self.root = false; + } + start + } +} + +/// Transform an XML stream of characters into a Rust object +pub struct Reader<T: IRead> { + evt: Event<'static>, + rdr: NsReader<T>, + buf: Vec<u8>, +} +impl<T: IRead> Reader<T> { + async fn new(mut rdr: NsReader<T>) -> Result<Self, ParsingError> { + let mut buf: Vec<u8> = vec![]; + let evt = rdr.read_event_into_async(&mut buf).await?.into_owned(); + buf.clear(); + Ok(Self { evt, rdr, buf }) + } + + fn peek(&self) -> &Event<'static> { + &self.evt + } + + /// skip tag. Can't skip end, can't skip eof. + async fn skip(&mut self) -> Result<Event<'static>, ParsingError> { + match &self.evt { + Event::Start(b) => { + let _span = self.rdr.read_to_end_into_async(b.to_end().name(), &mut self.buf).await?; + self.next().await + }, + Event::End(_) => Err(ParsingError::WrongToken), + Event::Eof => Err(ParsingError::Eof), + _ => self.next().await, + } + } + + /// read one more tag + 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.evt, evt); + Ok(old_evt) + } + + + /// check if this is the desired tag + fn is_tag(&self, ns: &[u8], key: &str) -> bool { + let qname = match self.peek() { + Event::Start(bs) | Event::Empty(bs) => bs.name(), + Event::End(be) => be.name(), + _ => return false, + }; + + let (extr_ns, local) = self.rdr.resolve_element(qname); + + if local.into_inner() != key.as_bytes() { + return false + } + + match extr_ns { + ResolveResult::Bound(v) => v.into_inner() == ns, + _ => false, + } + } + + /// find start tag + async fn tag_start(&mut self, ns: &[u8], key: &str) -> Result<Event<'static>, ParsingError> { + loop { + match self.peek() { + Event::Start(b) if self.is_tag(ns, key) => break, + _ => { self.skip().await?; }, + } + } + self.next().await + } + + // find stop tag + async fn tag_stop(&mut self, ns: &[u8], key: &str) -> Result<Event<'static>, ParsingError> { + loop { + match self.peek() { + Event::End(b) if self.is_tag(ns, key) => break, + _ => { self.skip().await?; }, + } + } + self.next().await + } +} + |