From 1e3737a590e2b329afc2b5531cf4ae67fb48a571 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Thu, 29 Feb 2024 20:40:40 +0100 Subject: At least it compiles --- src/dav/encoder.rs | 208 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 177 insertions(+), 31 deletions(-) (limited to 'src/dav/encoder.rs') diff --git a/src/dav/encoder.rs b/src/dav/encoder.rs index 552f183..ddef533 100644 --- a/src/dav/encoder.rs +++ b/src/dav/encoder.rs @@ -1,61 +1,205 @@ use std::io::Cursor; -use futures::stream::{StreamExt, TryStreamExt}; -use quick_xml::Error; +use quick_xml::Error as QError; use quick_xml::events::{Event, BytesEnd, BytesStart, BytesText}; use quick_xml::writer::{ElementWriter, Writer}; use quick_xml::name::PrefixDeclaration; use tokio::io::AsyncWrite; use super::types::*; -//@FIXME a cleaner way to manager namespace would be great -//but at the same time, the quick-xml library is not cooperating. -//So instead of writing many cursed workarounds - I tried, I am just hardcoding the namespaces... -pub trait Encode { - async fn write(&self, xml: &mut Writer) -> Result<(), Error>; +//-------------- TRAITS ---------------------- +/*pub trait DavWriter { + fn create_dav_element(&mut self, name: &str) -> ElementWriter; + fn child(w: &mut QWriter) -> impl DavWriter; + async fn error(&mut self, err: &E::Error) -> Result<(), QError>; +}*/ + +/// Basic encode trait to make a type encodable +pub trait QuickWritable> { + async fn write(&self, xml: &mut Writer, ctx: C) -> Result<(), QError>; +} + +pub trait Context { + fn child(&self) -> Self; + fn create_dav_element(&self, name: &str) -> BytesStart; + async fn hook_error(&self, err: &E::Error, xml: &mut Writer) -> Result<(), QError>; +} + +pub struct NoExtCtx { + root: bool +} +impl Context for NoExtCtx { + 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) -> Result<(), QError> { + unreachable!(); + } } -impl Encode for Href { - async fn write(&self, xml: &mut Writer) -> Result<(), Error> { - xml.create_element("D:href") + +//--------------------- ENCODING -------------------- + +// --- XML ROOTS +impl> QuickWritable for Multistatus { + async fn write(&self, xml: &mut Writer, ctx: C) -> Result<(), QError> { + let start = ctx.create_dav_element("multistatus"); + let end = start.to_end(); + + xml.write_event_async(Event::Start(start.clone())).await?; + for response in self.responses.iter() { + response.write(xml, ctx.child()).await?; + } + if let Some(description) = &self.responsedescription { + description.write(xml, ctx.child()).await?; + } + + xml.write_event_async(Event::End(end)).await?; + Ok(()) + } +} + + +// --- XML inner elements +impl> QuickWritable for Href { + async fn write(&self, xml: &mut Writer, _ctx: C) -> Result<(), QError> { + xml.create_element("href") .write_text_content_async(BytesText::new(&self.0)) .await?; Ok(()) } } -impl Encode for Multistatus { - async fn write(&self, xml: &mut Writer) -> Result<(), Error> { - xml.create_element("D:multistatus") - .with_attribute(("xmlns:D", "DAV:")) - .write_inner_content_async::<_, _, quick_xml::Error>(|inner_xml| async move { - for response in self.responses.iter() { - response.write(inner_xml).await?; +impl> QuickWritable for Response { + async fn write(&self, xml: &mut Writer, ctx: C) -> Result<(), QError> { + xml.create_element("response") + .write_inner_content_async::<_, _, QError>(|inner_xml| async move { + self.href.write(inner_xml, ctx.child()).await?; + self.status_or_propstat.write(inner_xml, ctx.child()).await?; + if let Some(error) = &self.error { + error.write(inner_xml, ctx.child()).await?; } - - if let Some(description) = &self.responsedescription { - description.write(inner_xml).await?; + if let Some(responsedescription) = &self.responsedescription { + responsedescription.write(inner_xml, ctx.child()).await?; } - + if let Some(location) = &self.location { + location.write(inner_xml, ctx.child()).await?; + } + Ok(inner_xml) }) + .await?; + + Ok(()) + } +} + +impl> QuickWritable for StatusOrPropstat { + async fn write(&self, xml: &mut Writer, ctx: C) -> Result<(), QError> { + match self { + Self::Status(status) => status.write(xml, ctx.child()).await, + Self::PropStat(propstat_list) => { + for propstat in propstat_list.iter() { + propstat.write(xml, ctx.child()).await?; + } + + Ok(()) + } + } + } +} + +impl> QuickWritable for Status { + async fn write(&self, xml: &mut Writer, ctx: C) -> Result<(), QError> { + xml.create_element("status") + .write_text_content_async( + BytesText::new(&format!("HTTP/1.1 {} {}", self.0.as_str(), self.0.canonical_reason().unwrap_or("No reason"))) + ) .await?; Ok(()) } } -impl Encode for Response { - async fn write(&self, xml: &mut Writer) -> Result<(), Error> { +impl> QuickWritable for ResponseDescription { + async fn write(&self, xml: &mut Writer, ctx: C) -> Result<(), QError> { + let start = ctx.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(()) + } +} + +impl> QuickWritable for Location { + async fn write(&self, xml: &mut Writer, ctx: C) -> Result<(), QError> { unimplemented!(); } } -impl Encode for ResponseDescription { - async fn write(&self, xml: &mut Writer) -> Result<(), Error> { - xml.create_element("D:responsedescription") - .write_text_content_async(BytesText::new(&self.0)) - .await?; +impl> QuickWritable for PropStat { + async fn write(&self, xml: &mut Writer, ctx: C) -> Result<(), QError> { + unimplemented!(); + } +} + +impl> QuickWritable for Error { + async fn write(&self, xml: &mut Writer, ctx: C) -> Result<(), QError> { + xml.create_element("error") + .write_inner_content_async::<_, _, QError>(|inner_xml| async move { + for violation in &self.0 { + violation.write(inner_xml, ctx.child()).await?; + } + + Ok(inner_xml) + }) + .await?; + + Ok(()) + } +} + +impl> QuickWritable for Violation { + async fn write(&self, xml: &mut Writer, ctx: C) -> Result<(), QError> { + match self { + Violation::LockTokenMatchesRequestUri => xml.create_element("lock-token-matches-request-uri").write_empty_async().await?, + Violation::LockTokenSubmitted(hrefs) => xml + .create_element("lock-token-submitted") + .write_inner_content_async::<_, _, QError>(|inner_xml| async move { + for href in hrefs { + href.write(inner_xml, ctx.child()).await?; + } + Ok(inner_xml) + } + ).await?, + Violation::NoConflictingLock(hrefs) => xml + .create_element("no-conflicting-lock") + .write_inner_content_async::<_, _, QError>(|inner_xml| async move { + for href in hrefs { + href.write(inner_xml, ctx.child()).await?; + } + Ok(inner_xml) + } + ).await?, + Violation::NoExternalEntities => xml.create_element("no-external-entities").write_empty_async().await?, + Violation::PreservedLiveProperties => xml.create_element("preserved-live-properties").write_empty_async().await?, + Violation::PropfindFiniteDepth => xml.create_element("propfind-finite-depth").write_empty_async().await?, + Violation::CannotModifyProtectedProperty => xml.create_element("cannot-modify-protected-property").write_empty_async().await?, + Violation::Extension(inner) => { + ctx.hook_error(inner, xml).await?; + xml + }, + }; Ok(()) } } @@ -74,7 +218,8 @@ mod tests { let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer); let mut writer = Writer::new_with_indent(&mut tokio_buffer, b' ', 4); - Href("/SOGo/dav/so/".into()).write(&mut writer).await.expect("xml serialization"); + let ctx = NoExtCtx{ root: true }; + Href("/SOGo/dav/so/".into()).write(&mut writer, ctx).await.expect("xml serialization"); tokio_buffer.flush().await.expect("tokio buffer flush"); assert_eq!(buffer.as_slice(), &b"/SOGo/dav/so/"[..]); @@ -87,8 +232,9 @@ mod tests { let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer); let mut writer = Writer::new_with_indent(&mut tokio_buffer, b' ', 4); - let xml: Multistatus = Multistatus { responses: vec![], responsedescription: Some(ResponseDescription("Hello world".into())) }; - xml.write(&mut writer).await.expect("xml serialization"); + let ctx = NoExtCtx{ root: true }; + let xml: Multistatus = Multistatus { responses: vec![], responsedescription: Some(ResponseDescription("Hello world".into())) }; + xml.write(&mut writer, ctx).await.expect("xml serialization"); tokio_buffer.flush().await.expect("tokio buffer flush"); let expected = r#" -- cgit v1.2.3