Compare commits
No commits in common. "c1e6f7e918eacaad9c8b1a4b27fcd4d6245aaf68" and "caf8b7506e1fb1ccd8e3f43c897a02db90397dde" have entirely different histories.
c1e6f7e918
...
caf8b7506e
|
@ -17,17 +17,6 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-recursion"
|
|
||||||
version = "1.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -283,7 +272,6 @@ dependencies = [
|
||||||
name = "peanuts"
|
name = "peanuts"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-recursion",
|
|
||||||
"circular",
|
"circular",
|
||||||
"futures",
|
"futures",
|
||||||
"nom",
|
"nom",
|
||||||
|
|
|
@ -6,7 +6,6 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-recursion = "1.1.1"
|
|
||||||
circular = { version = "0.3.0", path = "../circular" }
|
circular = { version = "0.3.0", path = "../circular" }
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
nom = "7.1.3"
|
nom = "7.1.3"
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
// elements resemble a final tree, including inherited namespace information
|
// elements resemble a final tree, including inherited namespace information
|
||||||
|
|
||||||
use std::{
|
use std::collections::{HashMap, HashSet};
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
convert::Infallible,
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
xml::{self, parsers_complete::Parser, Attribute},
|
xml::{self, Attribute},
|
||||||
};
|
};
|
||||||
|
|
||||||
// when are namespaces names chosen then if they are automatically calculated
|
// when are namespaces names chosen then if they are automatically calculated
|
||||||
|
@ -22,7 +18,7 @@ pub struct NamespaceDeclaration {
|
||||||
// names are qualified, they contain a reference to the namespace (held within the reader/writer)
|
// names are qualified, they contain a reference to the namespace (held within the reader/writer)
|
||||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||||
pub struct Name {
|
pub struct Name {
|
||||||
pub namespace: Option<String>,
|
pub namespace: String,
|
||||||
pub local_name: String,
|
pub local_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,33 +50,6 @@ pub struct Element {
|
||||||
pub content: Vec<Content>,
|
pub content: Vec<Content>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn escape_str(s: &str) -> String {
|
|
||||||
let mut string = String::new();
|
|
||||||
for str in s.split_inclusive(|c| c == '<' || c == '&' || c == '>') {
|
|
||||||
if let Some(str) = str.strip_suffix('<') {
|
|
||||||
if !str.is_empty() {
|
|
||||||
string.push_str(str)
|
|
||||||
}
|
|
||||||
string.push_str("<");
|
|
||||||
} else if let Some(str) = str.strip_suffix('&') {
|
|
||||||
if !str.is_empty() {
|
|
||||||
string.push_str(str)
|
|
||||||
}
|
|
||||||
string.push_str("&");
|
|
||||||
} else if let Some(str) = str.strip_suffix('>') {
|
|
||||||
if !str.is_empty() {
|
|
||||||
string.push_str(str)
|
|
||||||
}
|
|
||||||
string.push_str(">");
|
|
||||||
} else {
|
|
||||||
if !str.is_empty() {
|
|
||||||
string.push_str(str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
string
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl<'s> TryFrom<xml::Element<'s>> for Element<'s> {
|
// impl<'s> TryFrom<xml::Element<'s>> for Element<'s> {
|
||||||
// type Error = Error;
|
// type Error = Error;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ pub enum Error {
|
||||||
MismatchedEndTag(Name, Name),
|
MismatchedEndTag(Name, Name),
|
||||||
NotInElement(String),
|
NotInElement(String),
|
||||||
ExtraData(String),
|
ExtraData(String),
|
||||||
UndeclaredNamespace(String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
impl From<std::io::Error> for Error {
|
||||||
|
|
|
@ -48,7 +48,7 @@ where
|
||||||
Ok(self.inner.read_buf(&mut self.buffer).await?)
|
Ok(self.inner.read_buf(&mut self.buffer).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_prolog<'s>(&'s mut self) -> Result<()> {
|
async fn read_prolog<'s>(&'s mut self) -> Result<()> {
|
||||||
loop {
|
loop {
|
||||||
self.read_buf().await?;
|
self.read_buf().await?;
|
||||||
let input = str::from_utf8(self.buffer.data())?;
|
let input = str::from_utf8(self.buffer.data())?;
|
||||||
|
@ -68,7 +68,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_start_tag<'s>(&'s mut self) -> Result<Element> {
|
async fn read_start_tag<'s>(&'s mut self) -> Result<Element> {
|
||||||
loop {
|
loop {
|
||||||
self.read_buf().await?;
|
self.read_buf().await?;
|
||||||
let input = str::from_utf8(self.buffer.data())?;
|
let input = str::from_utf8(self.buffer.data())?;
|
||||||
|
@ -93,7 +93,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_end_tag<'s>(&'s mut self) -> Result<()> {
|
async fn read_end_tag<'s>(&'s mut self) -> Result<()> {
|
||||||
loop {
|
loop {
|
||||||
self.read_buf().await?;
|
self.read_buf().await?;
|
||||||
let input = str::from_utf8(self.buffer.data())?;
|
let input = str::from_utf8(self.buffer.data())?;
|
||||||
|
@ -118,7 +118,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_element<'s>(&'s mut self) -> Result<Element> {
|
async fn read_element<'s>(&'s mut self) -> Result<Element> {
|
||||||
loop {
|
loop {
|
||||||
self.read_buf().await?;
|
self.read_buf().await?;
|
||||||
let input = str::from_utf8(self.buffer.data())?;
|
let input = str::from_utf8(self.buffer.data())?;
|
||||||
|
@ -140,7 +140,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_content<'s>(&'s mut self) -> Result<Content> {
|
async fn read_content<'s>(&'s mut self) -> Result<Content> {
|
||||||
let mut last_char = false;
|
let mut last_char = false;
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
loop {
|
loop {
|
||||||
|
@ -312,7 +312,7 @@ impl<R> Reader<R> {
|
||||||
.chain(element_namespace_declarations.iter())
|
.chain(element_namespace_declarations.iter())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// element name
|
// element name and default attribute namespace
|
||||||
|
|
||||||
let element_namespace_declaration;
|
let element_namespace_declaration;
|
||||||
let element_local_name = s_tag.name.local_part().to_string();
|
let element_local_name = s_tag.name.local_part().to_string();
|
||||||
|
@ -330,8 +330,10 @@ impl<R> Reader<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let element_default_namespace =
|
let element_default_namespace = element_namespace_declaration
|
||||||
element_namespace_declaration.map(|decl| decl.namespace.clone());
|
.ok_or_else(|| Error::UnqualifiedNamespace(s_tag.name.to_string()))?
|
||||||
|
.namespace
|
||||||
|
.clone();
|
||||||
|
|
||||||
let element_name = Name {
|
let element_name = Name {
|
||||||
namespace: element_default_namespace,
|
namespace: element_default_namespace,
|
||||||
|
@ -359,25 +361,21 @@ impl<R> Reader<R> {
|
||||||
namespace_declaration.prefix.as_deref() == Some(prefix)
|
namespace_declaration.prefix.as_deref() == Some(prefix)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
None => attribute_namespace_declaration = None,
|
None => attribute_namespace_declaration = element_namespace_declaration,
|
||||||
}
|
}
|
||||||
let name;
|
|
||||||
if let Some(namespace_declaration) = attribute_namespace_declaration {
|
if let Some(namespace_declaration) = attribute_namespace_declaration {
|
||||||
name = Name {
|
let name = Name {
|
||||||
namespace: Some(namespace_declaration.namespace.clone()),
|
namespace: namespace_declaration.namespace.clone(),
|
||||||
local_name: attribute_local_name,
|
local_name: attribute_local_name,
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
name = Name {
|
|
||||||
namespace: None,
|
|
||||||
local_name: attribute_local_name,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let value = value.process()?;
|
let value = value.process()?;
|
||||||
// check for duplicate attribute
|
// check for duplicate attribute
|
||||||
if let Some(_value) = attributes.insert(name, value) {
|
if let Some(_value) = attributes.insert(name, value) {
|
||||||
return Err(Error::DuplicateAttribute(q_name.to_string()));
|
return Err(Error::DuplicateAttribute(q_name.to_string()));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::UnqualifiedNamespace(q_name.to_string()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
depth.push(element_name.clone());
|
depth.push(element_name.clone());
|
||||||
|
@ -417,7 +415,8 @@ impl<R> Reader<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let e_tag_namespace = e_tag_namespace_declaration
|
let e_tag_namespace = e_tag_namespace_declaration
|
||||||
.map(|decl| decl.namespace.clone())
|
.ok_or_else(|| Error::UnqualifiedNamespace(xml_e_tag.name.to_string()))?
|
||||||
|
.namespace
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let e_tag_name = Name {
|
let e_tag_name = Name {
|
||||||
|
@ -513,8 +512,10 @@ impl<R> Reader<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let element_default_namespace =
|
let element_default_namespace = element_namespace_declaration
|
||||||
element_namespace_declaration.map(|decl| decl.namespace.clone());
|
.ok_or_else(|| Error::UnqualifiedNamespace(xml_name.to_string()))?
|
||||||
|
.namespace
|
||||||
|
.clone();
|
||||||
|
|
||||||
let element_name = Name {
|
let element_name = Name {
|
||||||
namespace: element_default_namespace,
|
namespace: element_default_namespace,
|
||||||
|
@ -540,7 +541,10 @@ impl<R> Reader<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let e_tag_namespace = e_tag_namespace_declaration.map(|decl| decl.namespace.clone());
|
let e_tag_namespace = e_tag_namespace_declaration
|
||||||
|
.ok_or_else(|| Error::UnqualifiedNamespace(xml_name.to_string()))?
|
||||||
|
.namespace
|
||||||
|
.clone();
|
||||||
|
|
||||||
let e_tag_name = Name {
|
let e_tag_name = Name {
|
||||||
namespace: e_tag_namespace,
|
namespace: e_tag_namespace,
|
||||||
|
@ -573,25 +577,21 @@ impl<R> Reader<R> {
|
||||||
namespace_declaration.prefix.as_deref() == Some(prefix)
|
namespace_declaration.prefix.as_deref() == Some(prefix)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
None => attribute_namespace_declaration = None,
|
None => attribute_namespace_declaration = element_namespace_declaration,
|
||||||
}
|
}
|
||||||
let name;
|
|
||||||
if let Some(namespace_declaration) = attribute_namespace_declaration {
|
if let Some(namespace_declaration) = attribute_namespace_declaration {
|
||||||
name = Name {
|
let name = Name {
|
||||||
namespace: Some(namespace_declaration.namespace.clone()),
|
namespace: namespace_declaration.namespace.clone(),
|
||||||
local_name: attribute_local_name,
|
local_name: attribute_local_name,
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
name = Name {
|
|
||||||
namespace: None,
|
|
||||||
local_name: attribute_local_name,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let value = value.process()?;
|
let value = value.process()?;
|
||||||
// check for duplicate attribute
|
// check for duplicate attribute
|
||||||
if let Some(_value) = attributes.insert(name, value) {
|
if let Some(_value) = attributes.insert(name, value) {
|
||||||
return Err(Error::DuplicateAttribute(q_name.to_string()));
|
return Err(Error::DuplicateAttribute(q_name.to_string()));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::UnqualifiedNamespace(q_name.to_string()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
|
@ -674,21 +674,19 @@ impl<R: AsyncRead + Unpin> Stream for Reader<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod test {
|
mod test {
|
||||||
use futures::{sink::Buffer, StreamExt};
|
use futures::{sink::Buffer, StreamExt};
|
||||||
use tokio::io::AsyncRead;
|
use tokio::io::AsyncRead;
|
||||||
|
|
||||||
use crate::element::Element;
|
|
||||||
|
|
||||||
use super::Reader;
|
use super::Reader;
|
||||||
|
|
||||||
pub struct MockAsyncReader<'s> {
|
struct MockAsyncReader<'s> {
|
||||||
put: bool,
|
put: bool,
|
||||||
data: &'s str,
|
data: &'s str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> MockAsyncReader<'s> {
|
impl<'s> MockAsyncReader<'s> {
|
||||||
pub fn new(data: &'s str) -> Self {
|
fn new(data: &'s str) -> Self {
|
||||||
Self { put: false, data }
|
Self { put: false, data }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -707,7 +705,7 @@ pub(crate) mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const TEST_DOC: &'static str = "<xs:schema
|
const TEST_DOC: &'static str = "<xs:schema
|
||||||
xmlns:xs='http://www.w3.org/2001/XMLSchema'
|
xmlns:xs='http://www.w3.org/2001/XMLSchema'
|
||||||
targetNamespace='http://etherx.jabber.org/streams'
|
targetNamespace='http://etherx.jabber.org/streams'
|
||||||
xmlns='http://etherx.jabber.org/streams'
|
xmlns='http://etherx.jabber.org/streams'
|
||||||
|
|
312
src/writer.rs
312
src/writer.rs
|
@ -1,275 +1,53 @@
|
||||||
use std::{collections::HashSet, str::FromStr};
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use async_recursion::async_recursion;
|
|
||||||
use futures::Sink;
|
use futures::Sink;
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
use tokio::io::AsyncWrite;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
element::{escape_str, Content, Element, Name, NamespaceDeclaration},
|
element::{Element, Name, NamespaceDeclaration},
|
||||||
error::Error,
|
error::Error,
|
||||||
xml::{self, composers::Composer, parsers_complete::Parser, ETag},
|
xml::{self, composers::Composer, parsers_complete::Parser, ETag},
|
||||||
Result,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// pub struct Writer<W, C = Composer> {
|
// pub struct Writer<W, C = Composer> {
|
||||||
pub struct Writer<W> {
|
pub struct Writer<W> {
|
||||||
inner: W,
|
inner: W,
|
||||||
depth: Vec<Name>,
|
depth: Vec<Name>,
|
||||||
namespace_declarations: Vec<HashSet<NamespaceDeclaration>>,
|
namespaces: Vec<HashSet<NamespaceDeclaration>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> Writer<W> {
|
impl<W: AsyncWrite + Unpin> Writer<W> {
|
||||||
pub fn new(writer: W) -> Self {
|
pub async fn write(&mut self, element: Element) -> Result<(), Error> {
|
||||||
Self {
|
todo!()
|
||||||
inner: writer,
|
|
||||||
depth: Vec::new(),
|
|
||||||
namespace_declarations: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<()> {
|
pub async fn write_start(&mut self, element: Element) -> Result<(), Error> {
|
||||||
let namespace_declarations_stack: Vec<_> = self
|
todo!()
|
||||||
.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;
|
pub async fn write_end(&mut self) -> Result<(), Error> {
|
||||||
if let Some(prefix) = &prefix {
|
todo!()
|
||||||
name = xml::QName::PrefixedName(xml::PrefixedName {
|
// let e_tag;
|
||||||
prefix: xml::Prefix::parse_full(prefix)?,
|
// if let Some(name) = self.depth.pop() {
|
||||||
local_part: xml::LocalPart::parse_full(&element.name.local_name)?,
|
// if let Some(prefix) = name.namespace.prefix {
|
||||||
})
|
// e_tag = xml::ETag {
|
||||||
} else {
|
// name: xml::QName::PrefixedName(xml::PrefixedName {
|
||||||
name = xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(
|
// prefix: xml::Prefix::parse_full(&prefix)?,
|
||||||
&element.name.local_name,
|
// local_part: xml::LocalPart::parse_full(&name.name)?,
|
||||||
)?)
|
// }),
|
||||||
}
|
// };
|
||||||
|
// e_tag.write(&mut self.inner).await?;
|
||||||
let mut attributes = Vec::new();
|
// Ok(())
|
||||||
|
// } else {
|
||||||
for namespace_declaration in &element.namespace_declarations {
|
// e_tag = xml::ETag {
|
||||||
let ns_name = namespace_declaration
|
// name: xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(&name.name)?),
|
||||||
.prefix
|
// };
|
||||||
.as_ref()
|
// e_tag.write(&mut self.inner).await?;
|
||||||
.map(|prefix| -> Result<_> {
|
// Ok(())
|
||||||
Ok(xml::NSAttName::PrefixedAttName(xml::PrefixedAttName(
|
// }
|
||||||
xml::NCName::parse_full(&prefix)?,
|
// } else {
|
||||||
)))
|
// return Err(Error::NotInElement("".to_string()));
|
||||||
})
|
// }
|
||||||
.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());
|
|
||||||
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 s_tag = xml::STag { name, attributes };
|
|
||||||
|
|
||||||
s_tag.write(&mut self.inner).await?;
|
|
||||||
|
|
||||||
self.depth.push(element.name.clone());
|
|
||||||
self.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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn write_end(&mut self) -> Result<()> {
|
|
||||||
if let Some(name) = &self.depth.pop() {
|
|
||||||
let e_tag;
|
|
||||||
let namespace_declarations_stack: Vec<_> =
|
|
||||||
self.namespace_declarations.iter().flatten().collect();
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(prefix) = &prefix {
|
|
||||||
e_tag = xml::ETag {
|
|
||||||
name: xml::QName::PrefixedName(xml::PrefixedName {
|
|
||||||
prefix: xml::Prefix::parse_full(prefix)?,
|
|
||||||
local_part: xml::LocalPart::parse_full(&name.local_name)?,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
e_tag = xml::ETag {
|
|
||||||
name: xml::QName::UnprefixedName(xml::UnprefixedName::parse_full(
|
|
||||||
&name.local_name,
|
|
||||||
)?),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
e_tag.write(&mut self.inner).await?;
|
|
||||||
self.namespace_declarations.pop();
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
return Err(Error::NotInElement("".to_string()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,43 +57,25 @@ impl<W: AsyncWrite, E: Into<Element>> Sink<E> for Writer<W> {
|
||||||
fn poll_ready(
|
fn poll_ready(
|
||||||
self: std::pin::Pin<&mut Self>,
|
self: std::pin::Pin<&mut Self>,
|
||||||
cx: &mut std::task::Context<'_>,
|
cx: &mut std::task::Context<'_>,
|
||||||
) -> std::task::Poll<Result<()>> {
|
) -> std::task::Poll<Result<(), Self::Error>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_send(self: std::pin::Pin<&mut Self>, item: E) -> Result<()> {
|
fn start_send(self: std::pin::Pin<&mut Self>, item: E) -> Result<(), Self::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(
|
fn poll_flush(
|
||||||
self: std::pin::Pin<&mut Self>,
|
self: std::pin::Pin<&mut Self>,
|
||||||
cx: &mut std::task::Context<'_>,
|
cx: &mut std::task::Context<'_>,
|
||||||
) -> std::task::Poll<Result<()>> {
|
) -> std::task::Poll<Result<(), Self::Error>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_close(
|
fn poll_close(
|
||||||
self: std::pin::Pin<&mut Self>,
|
self: std::pin::Pin<&mut Self>,
|
||||||
cx: &mut std::task::Context<'_>,
|
cx: &mut std::task::Context<'_>,
|
||||||
) -> std::task::Poll<Result<()>> {
|
) -> std::task::Poll<Result<(), Self::Error>> {
|
||||||
todo!()
|
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
103
src/xml/mod.rs
103
src/xml/mod.rs
|
@ -1,6 +1,4 @@
|
||||||
use std::{char, convert::Infallible, ops::Deref, str::FromStr};
|
use std::{char, ops::Deref};
|
||||||
|
|
||||||
use parsers_complete::Parser;
|
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
|
@ -17,7 +15,7 @@ pub enum NSAttName<'s> {
|
||||||
|
|
||||||
/// [2] PrefixedAttName ::= 'xmlns:' NCName
|
/// [2] PrefixedAttName ::= 'xmlns:' NCName
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PrefixedAttName<'s>(pub NCName<'s>);
|
pub struct PrefixedAttName<'s>(NCName<'s>);
|
||||||
|
|
||||||
impl<'s> Deref for PrefixedAttName<'s> {
|
impl<'s> Deref for PrefixedAttName<'s> {
|
||||||
type Target = NCName<'s>;
|
type Target = NCName<'s>;
|
||||||
|
@ -230,14 +228,14 @@ pub enum EntityValue<'s> {
|
||||||
SingleQuoted(Vec<EntityValueData<'s>>),
|
SingleQuoted(Vec<EntityValueData<'s>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum AttValueData<'s> {
|
pub enum AttValueData<'s> {
|
||||||
String(&'s str),
|
String(&'s str),
|
||||||
Reference(Reference<'s>),
|
Reference(Reference<'s>),
|
||||||
}
|
}
|
||||||
/// [10] AttValue ::= '"' ([^<&"] | Reference)* '"'
|
/// [10] AttValue ::= '"' ([^<&"] | Reference)* '"'
|
||||||
/// | "'" ([^<&'] | Reference)* "'"
|
/// | "'" ([^<&'] | Reference)* "'"
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum AttValue<'s> {
|
pub enum AttValue<'s> {
|
||||||
DoubleQuoted(Vec<AttValueData<'s>>),
|
DoubleQuoted(Vec<AttValueData<'s>>),
|
||||||
SingleQuoted(Vec<AttValueData<'s>>),
|
SingleQuoted(Vec<AttValueData<'s>>),
|
||||||
|
@ -261,34 +259,6 @@ impl<'s> AttValue<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> From<&'s str> for AttValue<'s> {
|
|
||||||
fn from(s: &'s str) -> AttValue<'s> {
|
|
||||||
let mut data = Vec::new();
|
|
||||||
for str in s.split_inclusive(|c| c == '<' || c == '"') {
|
|
||||||
if let Some(str) = str.strip_suffix('<') {
|
|
||||||
if !str.is_empty() {
|
|
||||||
data.push(AttValueData::String(str))
|
|
||||||
}
|
|
||||||
data.push(AttValueData::Reference(Reference::EntityRef(EntityRef(
|
|
||||||
Name::parse_full("lt").unwrap(),
|
|
||||||
))))
|
|
||||||
} else if let Some(str) = str.strip_suffix('"') {
|
|
||||||
if !str.is_empty() {
|
|
||||||
data.push(AttValueData::String(str))
|
|
||||||
}
|
|
||||||
data.push(AttValueData::Reference(Reference::EntityRef(EntityRef(
|
|
||||||
Name::parse_full("quot").unwrap(),
|
|
||||||
))))
|
|
||||||
} else {
|
|
||||||
if !str.is_empty() {
|
|
||||||
data.push(AttValueData::String(str))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AttValue::DoubleQuoted(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
|
/// [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SystemLiteral<'s> {
|
pub enum SystemLiteral<'s> {
|
||||||
|
@ -703,7 +673,7 @@ pub struct IgnoreSectContents<'s> {
|
||||||
pub struct Ignore<'s>(&'s str);
|
pub struct Ignore<'s>(&'s str);
|
||||||
|
|
||||||
/// [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
|
/// [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum CharRef<'s> {
|
pub enum CharRef<'s> {
|
||||||
Decimal(&'s str),
|
Decimal(&'s str),
|
||||||
Hexadecimal(&'s str),
|
Hexadecimal(&'s str),
|
||||||
|
@ -736,7 +706,7 @@ impl<'s> CharRef<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [67] Reference ::= EntityRef | CharRef
|
/// [67] Reference ::= EntityRef | CharRef
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Reference<'s> {
|
pub enum Reference<'s> {
|
||||||
EntityRef(EntityRef<'s>),
|
EntityRef(EntityRef<'s>),
|
||||||
CharRef(CharRef<'s>),
|
CharRef(CharRef<'s>),
|
||||||
|
@ -759,8 +729,8 @@ impl<'s> Reference<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [68] EntityRef ::= '&' Name ';'
|
/// [68] EntityRef ::= '&' Name ';'
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct EntityRef<'s>(pub(crate) Name<'s>);
|
pub struct EntityRef<'s>(Name<'s>);
|
||||||
|
|
||||||
impl<'s> Deref for EntityRef<'s> {
|
impl<'s> Deref for EntityRef<'s> {
|
||||||
type Target = Name<'s>;
|
type Target = Name<'s>;
|
||||||
|
@ -865,60 +835,3 @@ pub struct NotationDecl<'s> {
|
||||||
/// [83] PublicID ::= 'PUBLIC' S PubidLiteral
|
/// [83] PublicID ::= 'PUBLIC' S PubidLiteral
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PublicID<'s>(PubidLiteral<'s>);
|
pub struct PublicID<'s>(PubidLiteral<'s>);
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::{AttValue, AttValueData, EntityRef, Name, Reference};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn att_value_from_str() {
|
|
||||||
assert_eq!(
|
|
||||||
AttValue::from("hsdaflaskdf<laksdf<abdsf"),
|
|
||||||
AttValue::DoubleQuoted(vec![
|
|
||||||
AttValueData::String("hsdaflaskdf"),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("lt")))),
|
|
||||||
AttValueData::String("laksdf"),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("lt")))),
|
|
||||||
AttValueData::String("abdsf"),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
AttValue::from("hsdaflaskdf<laksdf\"abdsf"),
|
|
||||||
AttValue::DoubleQuoted(vec![
|
|
||||||
AttValueData::String("hsdaflaskdf"),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("lt")))),
|
|
||||||
AttValueData::String("laksdf"),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("quot")))),
|
|
||||||
AttValueData::String("abdsf"),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
AttValue::from("hsdaflaskdf<laksdf\""),
|
|
||||||
AttValue::DoubleQuoted(vec![
|
|
||||||
AttValueData::String("hsdaflaskdf"),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("lt")))),
|
|
||||||
AttValueData::String("laksdf"),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("quot")))),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
AttValue::from("hsdaflaskdf\"<<laksdf\""),
|
|
||||||
AttValue::DoubleQuoted(vec![
|
|
||||||
AttValueData::String("hsdaflaskdf"),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("quot")))),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("lt")))),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("lt")))),
|
|
||||||
AttValueData::String("laksdf"),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("quot")))),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
AttValue::from("<<\""),
|
|
||||||
AttValue::DoubleQuoted(vec![
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("lt")))),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("lt")))),
|
|
||||||
AttValueData::Reference(Reference::EntityRef(EntityRef(Name("quot")))),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue