aboutsummaryrefslogtreecommitdiffstats
path: root/src/writer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/writer.rs')
-rw-r--r--src/writer.rs160
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();
+ }
+}