diff options
Diffstat (limited to 'src/writer.rs')
-rw-r--r-- | src/writer.rs | 160 |
1 files changed, 149 insertions, 11 deletions
diff --git a/src/writer.rs b/src/writer.rs index 4881f57..dc5b48a 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -1,10 +1,11 @@ use std::{collections::HashSet, str::FromStr}; +use async_recursion::async_recursion; use futures::Sink; -use tokio::io::AsyncWrite; +use tokio::io::{AsyncWrite, AsyncWriteExt}; use crate::{ - element::{Element, Name, NamespaceDeclaration}, + element::{escape_str, Content, Element, Name, NamespaceDeclaration}, error::Error, xml::{self, composers::Composer, parsers_complete::Parser, ETag}, Result, @@ -17,12 +18,32 @@ pub struct Writer<W> { namespace_declarations: Vec<HashSet<NamespaceDeclaration>>, } -impl<W: AsyncWrite + Unpin> Writer<W> { - pub async fn write(&mut self, element: Element) -> Result<()> { - todo!() +impl<W> Writer<W> { + pub fn new(writer: W) -> Self { + Self { + inner: writer, + depth: Vec::new(), + namespace_declarations: Vec::new(), + } } +} - pub async fn write_start(&mut self, element: Element) -> Result<()> { +impl<W: AsyncWrite + Unpin + Send> Writer<W> { + #[async_recursion] + pub async fn write_element(&mut self, element: &Element) -> Result<()> { + if element.content.is_empty() { + self.write_empty(element).await?; + } else { + self.write_start(element).await?; + for content in &element.content { + self.write_content(content).await?; + } + self.write_end().await?; + } + Ok(()) + } + + pub async fn write_empty(&mut self, element: &Element) -> Result<()> { let namespace_declarations_stack: Vec<_> = self .namespace_declarations .iter() @@ -60,9 +81,97 @@ impl<W: AsyncWrite + Unpin> Writer<W> { .prefix .as_ref() .map(|prefix| -> Result<_> { - Ok(xml::NSAttName::PrefixedAttName( - xml::PrefixedAttName::parse_full(&prefix)?, - )) + Ok(xml::NSAttName::PrefixedAttName(xml::PrefixedAttName( + xml::NCName::parse_full(&prefix)?, + ))) + }) + .unwrap_or(Ok(xml::NSAttName::DefaultAttName))?; + let value = xml::AttValue::from(namespace_declaration.namespace.as_str()); + let xml_attribute = xml::Attribute::NamespaceDeclaration { ns_name, value }; + attributes.push(xml_attribute); + } + + for (name, value) in &element.attributes { + let prefix; + if let Some(namespace) = &name.namespace { + let name_namespace_declaration = namespace_declarations_stack + .iter() + .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace) + .ok_or(Error::UndeclaredNamespace(namespace.clone()))?; + prefix = name_namespace_declaration.prefix.as_ref(); + } else { + prefix = None + } + + let att_name; + if let Some(prefix) = &prefix { + att_name = xml::QName::PrefixedName(xml::PrefixedName { + prefix: xml::Prefix::parse_full(prefix)?, + local_part: xml::LocalPart::parse_full(&element.name.local_name)?, + }) + } else { + att_name = xml::QName::UnprefixedName(xml::UnprefixedName::parse_full( + &element.name.local_name, + )?) + } + + let value = xml::AttValue::from(value.as_str()); + + let xml_attribute = xml::Attribute::Attribute { + name: att_name, + value, + }; + attributes.push(xml_attribute); + } + + let tag = xml::EmptyElemTag { name, attributes }; + + tag.write(&mut self.inner).await?; + + Ok(()) + } + + pub async fn write_start(&mut self, element: &Element) -> Result<()> { + let namespace_declarations_stack: Vec<_> = self + .namespace_declarations + .iter() + .flatten() + .chain(&element.namespace_declarations) + .collect(); + + let prefix; + if let Some(namespace) = &element.name.namespace { + let name_namespace_declaration = namespace_declarations_stack + .iter() + .rfind(|namespace_declaration| namespace_declaration.namespace == *namespace) + .ok_or(Error::UndeclaredNamespace(namespace.clone()))?; + prefix = name_namespace_declaration.prefix.as_ref(); + } else { + prefix = None + } + + let name; + if let Some(prefix) = &prefix { + name = xml::QName::PrefixedName(xml::PrefixedName { + prefix: xml::Prefix::parse_full(prefix)?, + local_part: xml::LocalPart::parse_full(&element.name.local_name)?, + }) + } else { + name = xml::QName::UnprefixedName(xml::UnprefixedName::parse_full( + &element.name.local_name, + )?) + } + + let mut attributes = Vec::new(); + + for namespace_declaration in &element.namespace_declarations { + let ns_name = namespace_declaration + .prefix + .as_ref() + .map(|prefix| -> Result<_> { + Ok(xml::NSAttName::PrefixedAttName(xml::PrefixedAttName( + xml::NCName::parse_full(&prefix)?, + ))) }) .unwrap_or(Ok(xml::NSAttName::DefaultAttName))?; let value = xml::AttValue::from(namespace_declaration.namespace.as_str()); @@ -107,9 +216,20 @@ impl<W: AsyncWrite + Unpin> Writer<W> { s_tag.write(&mut self.inner).await?; - self.depth.push(element.name); + self.depth.push(element.name.clone()); self.namespace_declarations - .push(element.namespace_declarations); + .push(element.namespace_declarations.clone()); + Ok(()) + } + + pub async fn write_content(&mut self, content: &Content) -> Result<()> { + match content { + Content::Element(element) => self.write_element(element).await?, + Content::Text(text) => self.inner.write_all(escape_str(text).as_bytes()).await?, + // TODO: comments and PI + Content::PI => {} + Content::Comment(_) => {} + } Ok(()) } @@ -181,3 +301,21 @@ impl<W: AsyncWrite, E: Into<Element>> Sink<E> for Writer<W> { todo!() } } + +#[cfg(test)] +mod test { + use crate::{ + reader::{test::*, Reader}, + writer::Writer, + }; + + #[tokio::test] + async fn test_element_write() { + let mock = MockAsyncReader::new(TEST_DOC); + let mut reader = Reader::new(mock); + let element = reader.read_element().await.unwrap(); + let stdout = tokio::io::stdout(); + let mut writer = Writer::new(stdout); + writer.write_element(&element).await.unwrap(); + } +} |