aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@bunny.garden>2025-04-03 03:09:35 +0100
committerLibravatar cel 🌸 <cel@bunny.garden>2025-04-03 03:09:35 +0100
commit9c561014f3c4278c0991290c898713f8e9c928e8 (patch)
tree92c60a88b34909387c854c483a3c3f4bc41c27b9 /src
parent2b399fb59d17bc127fbcd1533a3c079bc86770e1 (diff)
downloadpeanuts-9c561014f3c4278c0991290c898713f8e9c928e8.tar.gz
peanuts-9c561014f3c4278c0991290c898713f8e9c928e8.tar.bz2
peanuts-9c561014f3c4278c0991290c898713f8e9c928e8.zip
feat: xml logging
Diffstat (limited to 'src')
-rw-r--r--src/element.rs8
-rw-r--r--src/endable.rs4
-rw-r--r--src/error.rs6
-rw-r--r--src/lib.rs1
-rw-r--r--src/loggable.rs67
-rw-r--r--src/reader.rs26
-rw-r--r--src/writer.rs42
-rw-r--r--src/xml/composers.rs1216
-rw-r--r--src/xml/mod.rs43
-rw-r--r--src/xml/parsers.rs70
-rw-r--r--src/xml/parsers_complete.rs70
11 files changed, 1464 insertions, 89 deletions
diff --git a/src/element.rs b/src/element.rs
index 55b860f..1c1366a 100644
--- a/src/element.rs
+++ b/src/element.rs
@@ -75,6 +75,12 @@ pub struct Element {
pub content: VecDeque<Content>,
}
+impl FromElement for Element {
+ fn from_element(element: Element) -> DeserializeResult<Self> {
+ Ok(element)
+ }
+}
+
impl Element {
pub fn identify(&self) -> (Option<&str>, &str) {
(self.name.namespace.as_deref(), &self.name.local_name)
@@ -483,11 +489,13 @@ impl ElementBuilder {
self
}
+ // TODO: use references for everything to avoid cloning
pub fn push_child(mut self, child: impl IntoElement) -> Self {
self.content.push(ContentBuilder::Element(child.builder()));
self
}
+ // TODO: better way for push_text to work, empty string should be empty element no matter what
pub fn push_text(mut self, text: impl ToString) -> Self {
self.content.push(ContentBuilder::Text(text.to_string()));
self
diff --git a/src/endable.rs b/src/endable.rs
index 6006080..6d842f3 100644
--- a/src/endable.rs
+++ b/src/endable.rs
@@ -30,4 +30,8 @@ impl<T> Endable<T> {
Ok(&mut self.inner)
}
}
+
+ pub fn ignore_end(&mut self) -> &mut T {
+ &mut self.inner
+ }
}
diff --git a/src/error.rs b/src/error.rs
index 9337556..ae4aa26 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,5 +1,6 @@
use std::{
collections::{HashMap, VecDeque},
+ fmt,
num::ParseIntError,
str::Utf8Error,
sync::Arc,
@@ -35,8 +36,13 @@ pub enum DeserializeError {
// not used by crate (yet), but may be used by consumers implementing FromElement
#[error("unexpected element: {0:?}")]
UnexpectedElement(Element),
+ #[error("attribute `{0}` is an empty string")]
+ AttributeEmptyString(String),
+ #[error("empty string")]
+ EmptyString,
}
+// TODO: add error context (usually the stanza)
#[derive(Error, Debug, Clone)]
pub enum Error {
#[error("io: {0}")]
diff --git a/src/lib.rs b/src/lib.rs
index 26a3f78..3b4a592 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,7 @@ pub mod declaration;
pub mod element;
mod endable;
mod error;
+mod loggable;
pub mod reader;
mod writer;
pub mod xml;
diff --git a/src/loggable.rs b/src/loggable.rs
new file mode 100644
index 0000000..dd69668
--- /dev/null
+++ b/src/loggable.rs
@@ -0,0 +1,67 @@
+use std::{fmt::Display, mem, pin::pin, task::Poll};
+
+use futures::ready;
+use pin_project::pin_project;
+pub use tokio::io::AsyncWrite;
+
+#[pin_project]
+#[derive(Debug)]
+pub struct Loggable<W> {
+ log_buffer: Vec<u8>,
+ #[pin]
+ writer: W,
+}
+
+impl<W> Loggable<W> {
+ pub fn new(writer: W) -> Self {
+ Self {
+ log_buffer: Vec::new(),
+ writer,
+ }
+ }
+
+ pub fn into_inner(self) -> W {
+ self.writer
+ }
+
+ pub fn take_log(&mut self) -> Vec<u8> {
+ let log: Vec<u8> = mem::replace(&mut self.log_buffer, Vec::new());
+ log
+ }
+}
+
+impl<W: AsyncWrite + Unpin + Send> Display for Loggable<W> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let str = str::from_utf8(&self.log_buffer).unwrap_or("buffer to string conversion failed");
+ f.write_str(str)
+ }
+}
+
+impl<W: AsyncWrite + Unpin + Send> AsyncWrite for Loggable<W> {
+ fn poll_write(
+ mut self: std::pin::Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ buf: &[u8],
+ ) -> std::task::Poll<Result<usize, std::io::Error>> {
+ let this = self.as_mut().project();
+ let ready = ready!(this.writer.poll_write(cx, buf));
+ self.log_buffer.extend_from_slice(buf);
+ Poll::Ready(ready)
+ }
+
+ fn poll_flush(
+ self: std::pin::Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ ) -> std::task::Poll<Result<(), std::io::Error>> {
+ let this = self.project();
+ Poll::Ready(ready!(this.writer.poll_flush(cx)))
+ }
+
+ fn poll_shutdown(
+ self: std::pin::Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ ) -> std::task::Poll<Result<(), std::io::Error>> {
+ let this = self.project();
+ Poll::Ready(ready!(this.writer.poll_shutdown(cx)))
+ }
+}
diff --git a/src/reader.rs b/src/reader.rs
index 824446a..9eb7c91 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -5,7 +5,7 @@ use std::{
str,
};
use tokio::io::{AsyncRead, AsyncReadExt};
-use tracing::debug;
+use tracing::{debug, info, trace};
use crate::{
declaration::{Declaration, VersionInfo},
@@ -70,10 +70,17 @@ where
loop {
let input = str::from_utf8(self.buffer.data())?;
match xml::Prolog::parse(input) {
- Ok((rest, (decl, _misc, _doctype_decl))) => {
+ Ok((
+ rest,
+ xml::Prolog {
+ xml_decl,
+ miscs: _,
+ doctype_decl: _,
+ },
+ )) => {
let len = self.buffer.available_data() - rest.as_bytes().len();
// TODO: return error if there is a doctype decl
- if let Some(decl) = decl {
+ if let Some(decl) = xml_decl {
let declaration = Declaration {
version_info: match *decl.version_info {
xml::VersionNum::One => VersionInfo::One,
@@ -84,9 +91,13 @@ where
.map(|encoding_decl| (**encoding_decl).to_string()),
sd_decl: decl.sd_decl.map(|sd_decl| *sd_decl),
};
+ let element_plain = &input[..len];
+ info!("read prolog: {}", element_plain);
self.buffer.consume(len);
return Ok(Some(declaration));
} else {
+ let element_plain = &input[..len];
+ info!("read prolog: {}", element_plain);
self.buffer.consume(len);
return Ok(None);
}
@@ -105,12 +116,13 @@ where
pub async fn read_start<'s, T: FromElement>(&'s mut self) -> Result<T> {
let element = self.read_start_tag().await?;
+ trace!("read element start: {:?}", element);
Ok(FromElement::from_element(element)?)
}
pub async fn read<'s, T: FromElement>(&'s mut self) -> Result<T> {
let element = self.read_element().await?;
- debug!("read element: {:?}", element);
+ trace!("read element: {:?}", element);
Ok(FromElement::from_element(element)?)
}
@@ -128,6 +140,8 @@ where
&mut self.namespace_declarations,
e,
)?;
+ let element_plain = &input[..len];
+ info!("read element start: {}", element_plain);
self.buffer.consume(len);
return Ok(element);
}
@@ -160,6 +174,8 @@ where
if self.depth.is_empty() {
self.root_ended = true
}
+ let element_plain = &input[..len];
+ info!("read element end: {}", element_plain);
self.buffer.consume(len);
return Ok(());
}
@@ -189,6 +205,8 @@ where
if self.depth.is_empty() {
self.root_ended = true
}
+ let element_plain = &input[..len];
+ info!("read element: {}", element_plain);
self.buffer.consume(len);
return Ok(element);
}
diff --git a/src/writer.rs b/src/writer.rs
index e82d674..7ed1775 100644
--- a/src/writer.rs
+++ b/src/writer.rs
@@ -2,12 +2,14 @@ use std::collections::HashSet;
use async_recursion::async_recursion;
use tokio::io::{AsyncWrite, AsyncWriteExt};
+use tracing::info;
use crate::{
declaration::{Declaration, VersionInfo},
element::{escape_str, Content, Element, IntoContent, IntoElement, Name, NamespaceDeclaration},
endable::Endable,
error::Error,
+ loggable::Loggable,
xml::{self, composers::Composer, parsers_complete::Parser},
Result, XMLNS_NS, XML_NS,
};
@@ -15,7 +17,7 @@ use crate::{
// pub struct Writer<W, C = Composer> {
#[derive(Debug)]
pub struct Writer<W> {
- inner: Endable<W>,
+ inner: Endable<Loggable<W>>,
depth: Vec<Name>,
namespace_declarations: Vec<HashSet<NamespaceDeclaration>>,
}
@@ -32,14 +34,14 @@ impl<W> Writer<W> {
namespace: XMLNS_NS.to_string(),
});
Self {
- inner: Endable::new(writer),
+ inner: Endable::new(Loggable::new(writer)),
depth: Vec::new(),
namespace_declarations: vec![default_declarations],
}
}
pub fn into_inner(self) -> W {
- self.inner.into_inner()
+ self.inner.into_inner().into_inner()
}
}
@@ -65,24 +67,48 @@ impl<W: AsyncWrite + Unpin + Send> Writer<W> {
pub async fn write_full(&mut self, into_element: &impl IntoElement) -> Result<()> {
let element = into_element.into_element();
- Ok(self.write_element(&element).await?)
+ self.write_element(&element).await?;
+ let bytes = &self.inner.ignore_end().take_log();
+ let log = str::from_utf8(bytes).unwrap_or("failed to convert bytes written to str");
+ info!("wrote element: {}", log);
+ Ok(())
}
pub async fn write_start(&mut self, into_element: &impl IntoElement) -> Result<()> {
let element = into_element.into_element();
- Ok(self.write_element_start(&element).await?)
+ self.write_element_start(&element).await?;
+ let bytes = &self.inner.ignore_end().take_log();
+ let log = str::from_utf8(bytes).unwrap_or("failed to convert bytes written to str");
+ info!("wrote element start: {}", log);
+ Ok(())
}
pub async fn write_all_content(&mut self, into_element: &impl IntoElement) -> Result<()> {
for content in &into_element.get_content() {
self.write_content(content).await?;
}
+ let bytes = &self.inner.ignore_end().take_log();
+ let log = str::from_utf8(bytes).unwrap_or("failed to convert bytes written to str");
+ info!("wrote element content: {}", log);
Ok(())
}
pub async fn write(&mut self, into_content: &impl IntoContent) -> Result<()> {
let content = into_content.into_content();
- Ok(self.write_content(&content).await?)
+ self.write_content(&content).await?;
+ let bytes = &self.inner.ignore_end().take_log();
+ let log = str::from_utf8(bytes).unwrap_or("failed to convert bytes written to str");
+ info!("wrote element: {}", log);
+ Ok(())
+ }
+
+ // pub async fn write_end(&mut self)
+ pub async fn write_end(&mut self) -> Result<()> {
+ self.write_end_tag().await?;
+ let bytes = &self.inner.ignore_end().take_log();
+ let log = str::from_utf8(bytes).unwrap_or("failed to convert bytes written to str");
+ info!("wrote element end: {}", log);
+ Ok(())
}
#[async_recursion]
@@ -94,7 +120,7 @@ impl<W: AsyncWrite + Unpin + Send> Writer<W> {
for content in &element.content {
self.write_content(content).await?;
}
- self.write_end().await?;
+ self.write_end_tag().await?;
}
Ok(())
}
@@ -334,7 +360,7 @@ impl<W: AsyncWrite + Unpin + Send> Writer<W> {
Ok(())
}
- pub async fn write_end(&mut self) -> Result<()> {
+ pub async fn write_end_tag(&mut self) -> Result<()> {
let writer = self.inner.try_as_mut()?;
if let Some(name) = &self.depth.pop() {
let e_tag;
diff --git a/src/xml/composers.rs b/src/xml/composers.rs
index 8299708..a47f007 100644
--- a/src/xml/composers.rs
+++ b/src/xml/composers.rs
@@ -1,4 +1,7 @@
-use std::io;
+use std::{
+ fmt::{self, Display, Formatter, Write},
+ io,
+};
use tokio::io::{AsyncWrite, AsyncWriteExt};
@@ -144,9 +147,9 @@ impl<'s> Composer<'s> for Document<'s> {
where
W: Unpin + AsyncWrite,
{
- self.0.write(writer).await?;
- self.1.write(writer).await?;
- for misc in &self.2 {
+ self.prolog.write(writer).await?;
+ self.element.write(writer).await?;
+ for misc in &self.miscs {
misc.write(writer).await?
}
Ok(())
@@ -490,13 +493,13 @@ impl<'s> Composer<'s> for Prolog<'s> {
where
W: Unpin + AsyncWrite,
{
- if let Some(xml_decl) = &self.0 {
+ if let Some(xml_decl) = &self.xml_decl {
xml_decl.write(writer).await?;
}
- for misc in &self.1 {
+ for misc in &self.miscs {
misc.write(writer).await?;
}
- if let Some((doctype_decl, miscs)) = &self.2 {
+ if let Some((doctype_decl, miscs)) = &self.doctype_decl {
doctype_decl.write(writer).await?;
for misc in miscs {
misc.write(writer).await?;
@@ -634,7 +637,7 @@ impl<'s> Composer<'s> for IntSubset<'s> {
where
W: Unpin + AsyncWrite,
{
- for declaration in self {
+ for declaration in &self.0 {
match declaration {
super::IntSubsetDeclaration::MarkupDecl(markup_decl) => {
markup_decl.write(writer).await?
@@ -684,7 +687,7 @@ impl<'s> Composer<'s> for ExtSubsetDecl<'s> {
where
W: Unpin + AsyncWrite,
{
- for declaration in self {
+ for declaration in &self.0 {
match declaration {
ExtSubsetDeclaration::MarkupDecl(markup_decl) => markup_decl.write(writer).await?,
ExtSubsetDeclaration::ConditionalSect(conditional_sect) => {
@@ -1495,3 +1498,1198 @@ impl<'s> Composer<'s> for PublicID<'s> {
Ok(())
}
}
+
+/// [1] NSAttName ::= PrefixedAttName | DefaultAttName
+impl<'s> Display for NSAttName<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ NSAttName::PrefixedAttName(prefixed_att_name) => prefixed_att_name.fmt(f)?,
+ NSAttName::DefaultAttName => DefaultAttName.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// [2] PrefixedAttName ::= 'xmlns:' NCName
+impl<'s> Display for PrefixedAttName<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("xmlns:")?;
+ self.0.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [3] DefaultAttName ::= 'xmlns';
+impl Display for DefaultAttName {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("xmlns")?;
+ Ok(())
+ }
+}
+
+/// [4] NCName ::= Name - (Char* ':' Char*)
+impl<'s> Display for NCName<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str(self.0)?;
+ Ok(())
+ }
+}
+
+/// [7] QName ::= PrefixedName | UnprefixedName
+impl<'s> Display for QName<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ QName::PrefixedName(prefixed_name) => prefixed_name.fmt(f)?,
+ QName::UnprefixedName(unprefixed_name) => unprefixed_name.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// [8] PrefixedName ::= Prefix ':' LocalPart
+impl<'s> Display for PrefixedName<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ self.prefix.fmt(f)?;
+ f.write_str(":")?;
+ self.local_part.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [9] UnprefixedName ::= LocalPart
+impl<'s> Display for UnprefixedName<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [10] Prefix ::= NCName
+impl<'s> Display for Prefix<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [11] LocalPart ::= NCName
+impl<'s> Display for LocalPart<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)?;
+ Ok(())
+ }
+}
+
+// xml spec
+
+/// [1] document ::= prolog element Misc*
+impl<'s> Display for Document<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ self.prolog.fmt(f)?;
+ self.element.fmt(f)?;
+ for misc in &self.miscs {
+ misc.fmt(f)?
+ }
+ Ok(())
+ }
+}
+
+/// [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */
+impl Display for Char {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_char(self.0)?;
+ Ok(())
+ }
+}
+
+/// [3] S ::= (#x20 | #x9 | #xD | #xA)+
+impl<'s> Display for S {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("\u{20}")?;
+ Ok(())
+ }
+}
+
+/// [4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
+impl Display for NameStartChar {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_char(self.0)?;
+ Ok(())
+ }
+}
+
+/// [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
+impl Display for NameChar {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_char(self.0)?;
+ Ok(())
+ }
+}
+
+/// [5] Name ::= NameStartChar (NameChar)*
+impl<'s> Display for Name<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str(self.0)?;
+ Ok(())
+ }
+}
+
+/// [6] Names ::= Name (#x20 Name)*
+impl<'s> Display for Names<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ let mut first = true;
+ for name in &self.0 {
+ if !first {
+ f.write_str("\u{20}")?;
+ }
+ name.fmt(f)?;
+ if first {
+ first = false
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [7] Nmtoken ::= (NameChar)+
+impl<'s> Display for Nmtoken<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str(self.0)?;
+ Ok(())
+ }
+}
+
+/// [8] Nmtokens ::= Nmtoken (#x20 Nmtoken)*
+impl<'s> Display for Nmtokens<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ let mut first = true;
+ for nmtoken in &self.0 {
+ if !first {
+ f.write_str("\u{20}")?;
+ }
+ nmtoken.fmt(f)?;
+ if first {
+ first = false
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"'
+/// | "'" ([^%&'] | PEReference | Reference)* "'"
+impl<'s> Display for EntityValue<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ EntityValue::DoubleQuoted(entity_value_data) => {
+ f.write_str("\"")?;
+ for entity_value_data in entity_value_data {
+ match entity_value_data {
+ EntityValueData::String(s) => f.write_str(s)?,
+ EntityValueData::PEReference(pe_reference) => pe_reference.fmt(f)?,
+ EntityValueData::Reference(reference) => reference.fmt(f)?,
+ }
+ }
+ f.write_str("\"")?;
+ }
+ EntityValue::SingleQuoted(entity_value_data) => {
+ f.write_str("'")?;
+ for entity_value_data in entity_value_data {
+ match entity_value_data {
+ EntityValueData::String(s) => f.write_str(s)?,
+ EntityValueData::PEReference(pe_reference) => pe_reference.fmt(f)?,
+ EntityValueData::Reference(reference) => reference.fmt(f)?,
+ }
+ }
+ f.write_str("'")?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [10] AttValue ::= '"' ([^<&"] | Reference)* '"'
+/// | "'" ([^<&'] | Reference)* "'"
+impl<'s> Display for AttValue<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ AttValue::DoubleQuoted(att_value_data) => {
+ f.write_str("\"")?;
+ for att_value_data in att_value_data {
+ match att_value_data {
+ AttValueData::String(s) => f.write_str(s)?,
+ AttValueData::Reference(reference) => reference.fmt(f)?,
+ }
+ }
+ f.write_str("\"")?;
+ }
+ AttValue::SingleQuoted(att_value_data) => {
+ f.write_str("'")?;
+ for att_value_data in att_value_data {
+ match att_value_data {
+ AttValueData::String(s) => f.write_str(s)?,
+ AttValueData::Reference(reference) => reference.fmt(f)?,
+ }
+ }
+ f.write_str("'")?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
+impl<'s> Display for SystemLiteral<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ SystemLiteral::DoubleQuoted(s) => {
+ f.write_str("\"")?;
+ f.write_str(s)?;
+ f.write_str("\"")?;
+ }
+ SystemLiteral::SingleQuoted(s) => {
+ f.write_str("'")?;
+ f.write_str(s)?;
+ f.write_str("'")?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
+impl<'s> Display for PubidLiteral<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ PubidLiteral::DoubleQuoted(s) => {
+ f.write_str("\"")?;
+ f.write_str(s)?;
+ f.write_str("\"")?;
+ }
+ PubidLiteral::SingleQuoted(s) => {
+ f.write_str("'")?;
+ f.write_str(s)?;
+ f.write_str("'")?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
+impl Display for PubidChar {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_char(self.0)?;
+ Ok(())
+ }
+}
+
+/// [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
+impl<'s> Display for CharData<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str(self.0)?;
+ Ok(())
+ }
+}
+
+/// [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
+impl<'s> Display for Comment<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<!--")?;
+ f.write_str(self.0)?;
+ f.write_str("-->")?;
+ Ok(())
+ }
+}
+
+/// [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
+impl<'s> Display for PI<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<?")?;
+ self.target.fmt(f)?;
+ if let Some(instruction) = self.instruction {
+ S.fmt(f)?;
+ f.write_str(instruction)?;
+ }
+ f.write_str("?>")?;
+ Ok(())
+ }
+}
+
+/// [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
+impl<'s> Display for PITarget<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [18] CDSect ::= CDStart CData CDEnd
+impl<'s> Display for CDSect<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ CDStart.fmt(f)?;
+ self.0.fmt(f)?;
+ CDEnd.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [19] CDStart ::= '<![CDATA['
+impl Display for CDStart {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<![CDATA[")?;
+ Ok(())
+ }
+}
+
+/// [20] CData ::= (Char* - (Char* ']]>' Char*))
+impl<'s> Display for CData<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str(self.0)?;
+ Ok(())
+ }
+}
+
+/// [21] CDEnd ::= ']]>'
+impl Display for CDEnd {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("]]>")?;
+ Ok(())
+ }
+}
+
+/// [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)?
+impl<'s> Display for Prolog<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ if let Some(xml_decl) = &self.xml_decl {
+ xml_decl.fmt(f)?;
+ }
+ for misc in &self.miscs {
+ misc.fmt(f)?;
+ }
+ if let Some((doctype_decl, miscs)) = &self.doctype_decl {
+ doctype_decl.fmt(f)?;
+ for misc in miscs {
+ misc.fmt(f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
+impl<'s> Display for XMLDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<?xml")?;
+ self.version_info.fmt(f)?;
+ if let Some(encoding_decl) = &self.encoding_decl {
+ encoding_decl.fmt(f)?
+ }
+ if let Some(sd_decl) = &self.sd_decl {
+ sd_decl.fmt(f)?
+ }
+ f.write_str("?>")?;
+ Ok(())
+ }
+}
+
+/// [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
+impl Display for VersionInfo {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ S.fmt(f)?;
+ f.write_str("version")?;
+ Eq.fmt(f)?;
+ match self {
+ VersionInfo::SingleQuoted(version_num) => {
+ f.write_str("'")?;
+ version_num.fmt(f)?;
+ f.write_str("'")?;
+ }
+ VersionInfo::DoubleQuoted(version_num) => {
+ f.write_str("\"")?;
+ version_num.fmt(f)?;
+ f.write_str("\"")?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [25] Eq ::= S? '=' S?
+impl Display for Eq {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("=")?;
+ Ok(())
+ }
+}
+
+/// [26] VersionNum ::= '1.' [0-9]+
+impl Display for VersionNum {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ VersionNum::One => f.write_str("1.0")?,
+ VersionNum::OneDotOne => f.write_str("1.1")?,
+ }
+ Ok(())
+ }
+}
+
+/// [27] Misc ::= Comment | PI | S
+impl<'s> Display for Misc<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ Misc::Comment(comment) => comment.fmt(f)?,
+ Misc::PI(pi) => pi.fmt(f)?,
+ Misc::S => {}
+ }
+ Ok(())
+ }
+}
+
+/// [16] doctypedecl ::= '<!DOCTYPE' S QName (S ExternalID)? S? ('[' (markupdecl | PEReference | S)* ']' S?)? '>'
+/// [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>'
+impl<'s> Display for DoctypeDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<!DOCTYPE")?;
+ S.fmt(f)?;
+ self.name.fmt(f)?;
+ if let Some(external_id) = &self.external_id {
+ S.fmt(f)?;
+ external_id.fmt(f)?;
+ }
+ if let Some(int_subset) = &self.int_subset {
+ f.write_str("[")?;
+ int_subset.fmt(f)?;
+ f.write_str("]")?;
+ }
+ f.write_str(">")?;
+ Ok(())
+ }
+}
+
+/// [28a] DeclSep ::= PEReference | S
+impl<'s> Display for DeclSep<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ DeclSep::PEReference(pe_reference) => pe_reference.fmt(f)?,
+ DeclSep::S => S.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// [28b] intSubset ::= (markupdecl | DeclSep)*
+impl<'s> Display for IntSubset<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ for declaration in &self.0 {
+ match declaration {
+ super::IntSubsetDeclaration::MarkupDecl(markup_decl) => markup_decl.fmt(f)?,
+ super::IntSubsetDeclaration::DeclSep(decl_sep) => decl_sep.fmt(f)?,
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment
+impl<'s> Display for MarkupDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ MarkupDecl::Elementdecl(elementdecl) => elementdecl.fmt(f)?,
+ MarkupDecl::AttlistDecl(attlist_decl) => attlist_decl.fmt(f)?,
+ MarkupDecl::EntityDecl(entity_decl) => entity_decl.fmt(f)?,
+ MarkupDecl::NotationDecl(notation_decl) => notation_decl.fmt(f)?,
+ MarkupDecl::PI(pi) => pi.fmt(f)?,
+ MarkupDecl::Comment(comment) => comment.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// [30] extSubset ::= TextDecl? extSubsetDecl
+impl<'s> Display for ExtSubset<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ if let Some(text_decl) = &self.text_decl {
+ text_decl.fmt(f)?
+ }
+ self.ext_subset_decl.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [31] extSubsetDecl ::= ( markupdecl | conditionalSect | DeclSep)*
+impl<'s> Display for ExtSubsetDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ for declaration in &self.0 {
+ match declaration {
+ ExtSubsetDeclaration::MarkupDecl(markup_decl) => markup_decl.fmt(f)?,
+ ExtSubsetDeclaration::ConditionalSect(conditional_sect) => {
+ conditional_sect.fmt(f)?
+ }
+ ExtSubsetDeclaration::DeclSep(decl_sep) => decl_sep.fmt(f)?,
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"'))
+impl Display for SDDecl {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ S.fmt(f)?;
+ f.write_str("standalone")?;
+ Eq.fmt(f)?;
+ match self {
+ SDDecl::SingleQuoted(sd_decl) => {
+ f.write_str("'")?;
+ match sd_decl {
+ true => f.write_str("yes")?,
+ false => f.write_str("no")?,
+ }
+ f.write_str("'")?;
+ }
+ SDDecl::DoubleQuoted(sd_decl) => {
+ f.write_str("\"")?;
+ match sd_decl {
+ true => f.write_str("yes")?,
+ false => f.write_str("no")?,
+ }
+ f.write_str("\"")?;
+ }
+ }
+ Ok(())
+ }
+}
+
+// (Productions 33 through 38 have been removed.)
+
+/// [39] element ::= EmptyElemTag | STag content ETag
+impl<'s> Display for Element<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ Element::Empty(empty_elem_tag) => empty_elem_tag.fmt(f)?,
+ Element::NotEmpty(s_tag, content, e_tag) => {
+ s_tag.fmt(f)?;
+ content.fmt(f)?;
+ e_tag.fmt(f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [12] STag ::= '<' QName (S Attribute)* S? '>'
+/// [40] STag ::= '<' Name (S Attribute)* S? '>'
+impl<'s> Display for STag<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<")?;
+ self.name.fmt(f)?;
+ for attribute in &self.attributes {
+ S.fmt(f)?;
+ attribute.fmt(f)?;
+ }
+ f.write_str(">")?;
+ Ok(())
+ }
+}
+
+/// [15] Attribute ::= NSAttName Eq AttValue | QName Eq AttValue
+impl<'s> Display for Attribute<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ Attribute::NamespaceDeclaration { ns_name, value } => {
+ ns_name.fmt(f)?;
+ Eq.fmt(f)?;
+ value.fmt(f)?;
+ }
+ Attribute::Attribute { name, value } => {
+ name.fmt(f)?;
+ Eq.fmt(f)?;
+ value.fmt(f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [13] ETag ::= '</' QName S? '>'
+impl<'s> Display for ETag<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("</")?;
+ self.name.fmt(f)?;
+ f.write_str(">")?;
+ Ok(())
+ }
+}
+
+/// [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)*
+impl<'s> Display for Content<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ if let Some(char_data) = &self.char_data {
+ char_data.fmt(f)?;
+ }
+ for (content, char_data) in &self.content {
+ match content {
+ ContentItem::Element(element) => element.fmt(f)?,
+ ContentItem::Reference(reference) => reference.fmt(f)?,
+ ContentItem::CDSect(cd_sect) => cd_sect.fmt(f)?,
+ ContentItem::PI(pi) => pi.fmt(f)?,
+ ContentItem::Comment(comment) => comment.fmt(f)?,
+ // TODO: verify no split chardata
+ // _ => todo!("verify no split chardata"),
+ }
+ if let Some(char_data) = char_data {
+ char_data.fmt(f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [14] EmptyElemTag ::= '<' QName (S Attribute)* S? '/>'
+impl<'s> Display for EmptyElemTag<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<")?;
+ self.name.fmt(f)?;
+ for attribute in &self.attributes {
+ S.fmt(f)?;
+ attribute.fmt(f)?;
+ }
+ f.write_str("/>")?;
+ Ok(())
+ }
+}
+
+/// [17] elementdecl ::= '<!ELEMENT' S QName S contentspec S? '>'
+impl<'s> Display for Elementdecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<!ELEMENT")?;
+ S.fmt(f)?;
+ self.name.fmt(f)?;
+ S.fmt(f)?;
+ self.contentspec.fmt(f)?;
+ f.write_str(">")?;
+ Ok(())
+ }
+}
+
+/// [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
+impl<'s> Display for Contentspec<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ Contentspec::Empty => f.write_str("EMPTY")?,
+ Contentspec::Any => f.write_str("ANY")?,
+ Contentspec::Mixed(mixed) => mixed.fmt(f)?,
+ Contentspec::Children(children) => children.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// Occurence ::= ('?' | '*' | '+')?
+impl Display for Occurence {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ Occurence::Once => {}
+ Occurence::Optional => f.write_str("?")?,
+ Occurence::Many0 => f.write_str("*")?,
+ Occurence::Many1 => f.write_str("+")?,
+ }
+ Ok(())
+ }
+}
+
+/// [47] children ::= (choice | seq) ('?' | '*' | '+')?
+impl<'s> Display for Children<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match &self.kind {
+ ChildrenKind::Choice(choice) => choice.fmt(f)?,
+ ChildrenKind::Seq(seq) => seq.fmt(f)?,
+ }
+ self.occurence.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [18] cp ::= (QName | choice | seq) ('?' | '*' | '+')?
+impl<'s> Display for Cp<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match &self.kind {
+ CpKind::Name(q_name) => q_name.fmt(f)?,
+ CpKind::Choice(choice) => choice.fmt(f)?,
+ CpKind::Seq(seq) => seq.fmt(f)?,
+ }
+ self.occurence.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [49] choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')'
+impl<'s> Display for Choice<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("(")?;
+ let mut first = true;
+ for cp in &self.0 {
+ if !first {
+ f.write_str("|")?;
+ }
+ cp.fmt(f)?;
+ if first {
+ first = false
+ }
+ }
+ f.write_str(")")?;
+ Ok(())
+ }
+}
+
+/// [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')'
+impl<'s> Display for Seq<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("(")?;
+ let mut first = true;
+ for cp in &self.0 {
+ if !first {
+ f.write_str(",")?;
+ }
+ cp.fmt(f)?;
+ if first {
+ first = false
+ }
+ }
+ f.write_str(")")?;
+ Ok(())
+ }
+}
+
+/// [19] Mixed ::= '(' S? '#PCDATA' (S? '|' S? QName)* S? ')*' | '(' S? '#PCDATA' S? ')'
+impl<'s> Display for Mixed<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("(#PCDATA")?;
+ if !self.0.is_empty() {
+ for q_name in &self.0 {
+ f.write_str("|")?;
+ q_name.fmt(f)?;
+ }
+ f.write_str(")*")?;
+ } else {
+ f.write_str(")")?;
+ }
+ Ok(())
+ }
+}
+
+/// [20] AttlistDecl ::= '<!ATTLIST' S QName AttDef* S? '>'
+impl<'s> Display for AttlistDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<!ATTLIST")?;
+ S.fmt(f)?;
+ self.element_type.fmt(f)?;
+ for att_def in &self.att_defs {
+ att_def.fmt(f)?;
+ }
+ f.write_str(">")?;
+ Ok(())
+ }
+}
+
+/// [21] AttDef ::= S (QName | NSAttName) S AttType S DefaultDecl
+impl<'s> Display for AttDef<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ S.fmt(f)?;
+ match &self.name {
+ AttDefName::QName(q_name) => q_name.fmt(f)?,
+ AttDefName::NSAttName(ns_att_name) => ns_att_name.fmt(f)?,
+ }
+ S.fmt(f)?;
+ self.att_type.fmt(f)?;
+ S.fmt(f)?;
+ self.default_decl.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [54] AttType ::= StringType | TokenizedType | EnumeratedType
+impl<'s> Display for AttType<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ AttType::StringType => StringType.fmt(f)?,
+ AttType::TokenizedType(tokenized_type) => tokenized_type.fmt(f)?,
+ AttType::EnumeratedType(enumerated_type) => enumerated_type.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// [55] StringType ::= 'CDATA'
+impl Display for StringType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("CDATA")?;
+ Ok(())
+ }
+}
+
+/// [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS'
+impl Display for TokenizedType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ TokenizedType::ID => f.write_str("ID")?,
+ TokenizedType::IDRef => f.write_str("IDREF")?,
+ TokenizedType::IDRefs => f.write_str("IDREFS")?,
+ TokenizedType::Entity => f.write_str("ENTITY")?,
+ TokenizedType::Entities => f.write_str("ENTITIES")?,
+ TokenizedType::NMToken => f.write_str("NMTOKEN")?,
+ TokenizedType::NMTokens => f.write_str("NMTOKENS")?,
+ }
+ Ok(())
+ }
+}
+
+/// [57] EnumeratedType ::= NotationType | Enumeration
+impl<'s> Display for EnumeratedType<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ EnumeratedType::NotationType(notation_type) => notation_type.fmt(f)?,
+ EnumeratedType::Enumeration(enumeration) => enumeration.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
+impl<'s> Display for NotationType<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("NOTATION")?;
+ S.fmt(f)?;
+ f.write_str("(")?;
+ let mut first = true;
+ for name in &self.0 {
+ if !first {
+ f.write_str("|")?;
+ }
+ name.fmt(f)?;
+ if first {
+ first = false
+ }
+ }
+ f.write_str(")")?;
+ Ok(())
+ }
+}
+
+/// [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
+impl<'s> Display for Enumeration<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("(")?;
+ let mut first = true;
+ for nm_token in &self.0 {
+ if !first {
+ f.write_str("|")?;
+ }
+ nm_token.fmt(f)?;
+ if first {
+ first = false
+ }
+ }
+ f.write_str(")")?;
+ Ok(())
+ }
+}
+
+/// [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
+impl<'s> Display for DefaultDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ DefaultDecl::Required => f.write_str("#REQUIRED")?,
+ DefaultDecl::Implied => f.write_str("#IMPLIED")?,
+ DefaultDecl::Fixed(fixed, att_value) => {
+ if *fixed {
+ f.write_str("#FIXED")?;
+ S.fmt(f)?;
+ }
+ att_value.fmt(f)?
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [61] conditionalSect ::= includeSect | ignoreSect
+impl<'s> Display for ConditionalSect<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ ConditionalSect::IncludeSect(include_sect) => include_sect.fmt(f)?,
+ ConditionalSect::IgnoreSect(ignore_sect) => ignore_sect.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>'
+impl<'s> Display for IncludeSect<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<![INCLUDE[")?;
+ self.0.fmt(f)?;
+ f.write_str("]]>")?;
+ Ok(())
+ }
+}
+
+/// [63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
+impl<'s> Display for IgnoreSect<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<![IGNORE[")?;
+ for ignore_sect_contents in &self.0 {
+ ignore_sect_contents.fmt(f)?;
+ }
+ f.write_str("]]>")?;
+ Ok(())
+ }
+}
+
+/// [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)*
+impl<'s> Display for IgnoreSectContents<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ self.ignore.fmt(f)?;
+ for (ignore_sect_contents, ignore) in &self.ignore_list {
+ f.write_str("<![")?;
+ ignore_sect_contents.fmt(f)?;
+ f.write_str("]]>")?;
+ ignore.fmt(f)?;
+ }
+ Ok(())
+ }
+}
+
+/// [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*)
+impl<'s> Display for Ignore<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str(self.0)?;
+ Ok(())
+ }
+}
+
+/// [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
+impl<'s> Display for CharRef<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ CharRef::Decimal(decimal) => {
+ f.write_str("&#")?;
+ f.write_str(decimal)?;
+ f.write_str(";")?;
+ }
+ CharRef::Hexadecimal(hexadecimal) => {
+ f.write_str("&#x")?;
+ f.write_str(hexadecimal)?;
+ f.write_str(";")?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [67] Reference ::= EntityRef | CharRef
+impl<'s> Display for Reference<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ Reference::EntityRef(entity_ref) => entity_ref.fmt(f)?,
+ Reference::CharRef(char_ref) => char_ref.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// [68] EntityRef ::= '&' Name ';'
+impl<'s> Display for EntityRef<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("&")?;
+ self.0.fmt(f)?;
+ f.write_str(";")?;
+ Ok(())
+ }
+}
+
+/// [69] PEReference ::= '%' Name ';'
+impl<'s> Display for PEReference<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("%")?;
+ self.0.fmt(f)?;
+ f.write_str(";")?;
+ Ok(())
+ }
+}
+
+/// [70] EntityDecl ::= GEDecl | PEDecl
+impl<'s> Display for EntityDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ EntityDecl::GEDecl(ge_decl) => ge_decl.fmt(f)?,
+ EntityDecl::PEDecl(pe_decl) => pe_decl.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
+impl<'s> Display for GEDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<!ENTITY")?;
+ S.fmt(f)?;
+ self.name.fmt(f)?;
+ S.fmt(f)?;
+ self.entity_def.fmt(f)?;
+ f.write_str(">")?;
+ Ok(())
+ }
+}
+
+/// [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
+impl<'s> Display for PEDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<!ENTITY")?;
+ S.fmt(f)?;
+ f.write_str("%")?;
+ S.fmt(f)?;
+ self.name.fmt(f)?;
+ S.fmt(f)?;
+ self.pe_def.fmt(f)?;
+ f.write_str(">")?;
+ Ok(())
+ }
+}
+
+/// [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
+impl<'s> Display for EntityDef<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ EntityDef::EntityValue(entity_value) => entity_value.fmt(f)?,
+ EntityDef::ExternalID {
+ external_id,
+ n_data_decl,
+ } => {
+ external_id.fmt(f)?;
+ if let Some(n_data_decl) = n_data_decl {
+ n_data_decl.fmt(f)?;
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [74] PEDef ::= EntityValue | ExternalID
+impl<'s> Display for PEDef<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ PEDef::EntityValue(entity_value) => entity_value.fmt(f)?,
+ PEDef::ExternalID(external_id) => external_id.fmt(f)?,
+ }
+ Ok(())
+ }
+}
+
+/// [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral
+impl<'s> Display for ExternalID<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ ExternalID::SYSTEM { system_identifier } => {
+ f.write_str("SYSTEM")?;
+ S.fmt(f)?;
+ system_identifier.fmt(f)?;
+ }
+ ExternalID::PUBLIC {
+ public_identifier,
+ system_identifier,
+ } => {
+ f.write_str("PUBLIC")?;
+ S.fmt(f)?;
+ public_identifier.fmt(f)?;
+ S.fmt(f)?;
+ system_identifier.fmt(f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+/// [76] NDataDecl ::= S 'NDATA' S Name
+impl<'s> Display for NDataDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ S.fmt(f)?;
+ f.write_str("NDATA")?;
+ S.fmt(f)?;
+ self.0.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
+impl<'s> Display for TextDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<?xml")?;
+ if let Some(version_info) = &self.version_info {
+ version_info.fmt(f)?;
+ }
+ self.encoding_decl.fmt(f)?;
+ f.write_str("?>")?;
+ Ok(())
+ }
+}
+
+/// [78] extParsedEnt ::= TextDecl? content
+impl<'s> Display for ExtParsedEnt<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ if let Some(text_decl) = &self.text_decl {
+ text_decl.fmt(f)?;
+ }
+ self.content.fmt(f)?;
+ Ok(())
+ }
+}
+
+/// [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName
+impl<'s> Display for EncodingDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ S.fmt(f)?;
+ f.write_str("encoding")?;
+ Eq.fmt(f)?;
+ f.write_str("\"")?;
+ self.0.fmt(f)?;
+ f.write_str("\"")?;
+ Ok(())
+ }
+}
+
+/// [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
+impl<'s> Display for EncName<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str(self.0)?;
+ Ok(())
+ }
+}
+
+/// [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>'
+impl<'s> Display for NotationDecl<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("<!NOTATION")?;
+ S.fmt(f)?;
+ self.name.fmt(f)?;
+ S.fmt(f)?;
+ match &self.id {
+ NotationDeclID::External(external_id) => external_id.fmt(f)?,
+ NotationDeclID::Public(public_id) => public_id.fmt(f)?,
+ }
+ f.write_str(">")?;
+ Ok(())
+ }
+}
+
+/// [83] PublicID ::= 'PUBLIC' S PubidLiteral
+impl<'s> Display for PublicID<'s> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("PUBLIC")?;
+ S.fmt(f)?;
+ self.0.fmt(f)?;
+ Ok(())
+ }
+}
diff --git a/src/xml/mod.rs b/src/xml/mod.rs
index 3f7dc79..b0d9056 100644
--- a/src/xml/mod.rs
+++ b/src/xml/mod.rs
@@ -66,16 +66,16 @@ impl<'s> QName<'s> {
}
}
-impl<'s> ToString for QName<'s> {
- fn to_string(&self) -> String {
- match self {
- QName::PrefixedName(prefixed_name) => {
- format!("{}:{}", **prefixed_name.prefix, **prefixed_name.local_part)
- }
- QName::UnprefixedName(unprefixed_name) => unprefixed_name.to_string(),
- }
- }
-}
+// impl<'s> ToString for QName<'s> {
+// fn to_string(&self) -> String {
+// match self {
+// QName::PrefixedName(prefixed_name) => {
+// format!("{}:{}", **prefixed_name.prefix, **prefixed_name.local_part)
+// }
+// QName::UnprefixedName(unprefixed_name) => unprefixed_name.to_string(),
+// }
+// }
+// }
/// [8] PrefixedName ::= Prefix ':' LocalPart
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -123,7 +123,12 @@ impl<'s> Deref for LocalPart<'s> {
// xml spec
/// [1] document ::= prolog element Misc*
-pub type Document<'s> = (Prolog<'s>, Element<'s>, Vec<Misc<'s>>);
+#[derive(Debug)]
+pub struct Document<'s> {
+ prolog: Prolog<'s>,
+ element: Element<'s>,
+ miscs: Vec<Misc<'s>>,
+}
/// [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */
#[repr(transparent)]
@@ -380,11 +385,12 @@ impl<'s> Deref for CData<'s> {
pub struct CDEnd;
/// [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)?
-pub type Prolog<'s> = (
- Option<XMLDecl<'s>>,
- Vec<Misc<'s>>,
- Option<(DoctypeDecl<'s>, Vec<Misc<'s>>)>,
-);
+#[derive(Debug)]
+pub struct Prolog<'s> {
+ pub(crate) xml_decl: Option<XMLDecl<'s>>,
+ pub(crate) miscs: Vec<Misc<'s>>,
+ pub(crate) doctype_decl: Option<(DoctypeDecl<'s>, Vec<Misc<'s>>)>,
+}
/// [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
#[derive(Debug)]
@@ -456,7 +462,8 @@ pub enum IntSubsetDeclaration<'s> {
}
/// from [16] intSubset ::= (markupdecl | PEReference | S)*
/// [28b] intSubset ::= (markupdecl | DeclSep)*
-pub type IntSubset<'s> = Vec<IntSubsetDeclaration<'s>>;
+#[derive(Debug)]
+pub struct IntSubset<'s>(Vec<IntSubsetDeclaration<'s>>);
/// [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment
#[derive(Debug)]
@@ -481,7 +488,7 @@ pub enum ExtSubsetDeclaration<'s> {
DeclSep(DeclSep<'s>),
}
/// [31] extSubsetDecl ::= ( markupdecl | conditionalSect | DeclSep)*
-type ExtSubsetDecl<'s> = Vec<ExtSubsetDeclaration<'s>>;
+pub struct ExtSubsetDecl<'s>(Vec<ExtSubsetDeclaration<'s>>);
/// [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"'))
#[derive(Debug, Clone)]
diff --git a/src/xml/parsers.rs b/src/xml/parsers.rs
index 87858a1..79d72b4 100644
--- a/src/xml/parsers.rs
+++ b/src/xml/parsers.rs
@@ -146,7 +146,14 @@ impl<'s> Parser<'s> for Document<'s> {
type Output = Document<'s>;
fn parse(input: &'s str) -> IResult<&'s str, Document<'s>> {
- tuple((Prolog::parse, Element::parse, many0(Misc::parse)))(input)
+ map(
+ tuple((Prolog::parse, Element::parse, many0(Misc::parse))),
+ |(prolog, element, miscs)| Document {
+ prolog,
+ element,
+ miscs,
+ },
+ )(input)
}
}
@@ -518,11 +525,18 @@ impl<'s> Parser<'s> for Prolog<'s> {
type Output = Prolog<'s>;
fn parse(input: &'s str) -> IResult<&'s str, Prolog<'s>> {
- tuple((
- opt(XMLDecl::parse),
- many0(Misc::parse),
- opt(tuple((DoctypeDecl::parse, many0(Misc::parse)))),
- ))(input)
+ map(
+ tuple((
+ opt(XMLDecl::parse),
+ many0(Misc::parse),
+ opt(tuple((DoctypeDecl::parse, many0(Misc::parse)))),
+ )),
+ |(xml_decl, miscs, doctype_decl)| Prolog {
+ xml_decl,
+ miscs,
+ doctype_decl,
+ },
+ )(input)
}
}
@@ -661,14 +675,17 @@ impl<'s> Parser<'s> for IntSubset<'s> {
type Output = IntSubset<'s>;
fn parse(input: &'s str) -> IResult<&'s str, IntSubset<'s>> {
- many0(alt((
- map(MarkupDecl::parse, |markup_decl| {
- IntSubsetDeclaration::MarkupDecl(markup_decl)
- }),
- map(DeclSep::parse, |decl_sep| {
- IntSubsetDeclaration::DeclSep(decl_sep)
- }),
- )))(input)
+ map(
+ many0(alt((
+ map(MarkupDecl::parse, |markup_decl| {
+ IntSubsetDeclaration::MarkupDecl(markup_decl)
+ }),
+ map(DeclSep::parse, |decl_sep| {
+ IntSubsetDeclaration::DeclSep(decl_sep)
+ }),
+ ))),
+ |declarations| IntSubset(declarations),
+ )(input)
}
}
@@ -716,17 +733,20 @@ impl<'s> Parser<'s> for ExtSubsetDecl<'s> {
type Output = ExtSubsetDecl<'s>;
fn parse(input: &'s str) -> IResult<&'s str, ExtSubsetDecl<'s>> {
- many0(alt((
- map(MarkupDecl::parse, |markup_decl| {
- ExtSubsetDeclaration::MarkupDecl(markup_decl)
- }),
- map(ConditionalSect::parse, |conditional_sect| {
- ExtSubsetDeclaration::ConditionalSect(conditional_sect)
- }),
- map(DeclSep::parse, |decl_sep| {
- ExtSubsetDeclaration::DeclSep(decl_sep)
- }),
- )))(input)
+ map(
+ many0(alt((
+ map(MarkupDecl::parse, |markup_decl| {
+ ExtSubsetDeclaration::MarkupDecl(markup_decl)
+ }),
+ map(ConditionalSect::parse, |conditional_sect| {
+ ExtSubsetDeclaration::ConditionalSect(conditional_sect)
+ }),
+ map(DeclSep::parse, |decl_sep| {
+ ExtSubsetDeclaration::DeclSep(decl_sep)
+ }),
+ ))),
+ |declarations| ExtSubsetDecl(declarations),
+ )(input)
}
}
diff --git a/src/xml/parsers_complete.rs b/src/xml/parsers_complete.rs
index cdc4fed..f18d0ff 100644
--- a/src/xml/parsers_complete.rs
+++ b/src/xml/parsers_complete.rs
@@ -156,7 +156,14 @@ impl<'s> Parser<'s> for Document<'s> {
type Output = Document<'s>;
fn parse(input: &'s str) -> IResult<&'s str, Document<'s>> {
- tuple((Prolog::parse, Element::parse, many0(Misc::parse)))(input)
+ map(
+ tuple((Prolog::parse, Element::parse, many0(Misc::parse))),
+ |(prolog, element, miscs)| Document {
+ prolog,
+ element,
+ miscs,
+ },
+ )(input)
}
}
@@ -528,11 +535,18 @@ impl<'s> Parser<'s> for Prolog<'s> {
type Output = Prolog<'s>;
fn parse(input: &'s str) -> IResult<&'s str, Prolog<'s>> {
- tuple((
- opt(XMLDecl::parse),
- many0(Misc::parse),
- opt(tuple((DoctypeDecl::parse, many0(Misc::parse)))),
- ))(input)
+ map(
+ tuple((
+ opt(XMLDecl::parse),
+ many0(Misc::parse),
+ opt(tuple((DoctypeDecl::parse, many0(Misc::parse)))),
+ )),
+ |(xml_decl, miscs, doctype_decl)| Prolog {
+ xml_decl,
+ miscs,
+ doctype_decl,
+ },
+ )(input)
}
}
@@ -671,14 +685,17 @@ impl<'s> Parser<'s> for IntSubset<'s> {
type Output = IntSubset<'s>;
fn parse(input: &'s str) -> IResult<&'s str, IntSubset<'s>> {
- many0(alt((
- map(MarkupDecl::parse, |markup_decl| {
- IntSubsetDeclaration::MarkupDecl(markup_decl)
- }),
- map(DeclSep::parse, |decl_sep| {
- IntSubsetDeclaration::DeclSep(decl_sep)
- }),
- )))(input)
+ map(
+ many0(alt((
+ map(MarkupDecl::parse, |markup_decl| {
+ IntSubsetDeclaration::MarkupDecl(markup_decl)
+ }),
+ map(DeclSep::parse, |decl_sep| {
+ IntSubsetDeclaration::DeclSep(decl_sep)
+ }),
+ ))),
+ |declarations| IntSubset(declarations),
+ )(input)
}
}
@@ -726,17 +743,20 @@ impl<'s> Parser<'s> for ExtSubsetDecl<'s> {
type Output = ExtSubsetDecl<'s>;
fn parse(input: &'s str) -> IResult<&'s str, ExtSubsetDecl<'s>> {
- many0(alt((
- map(MarkupDecl::parse, |markup_decl| {
- ExtSubsetDeclaration::MarkupDecl(markup_decl)
- }),
- map(ConditionalSect::parse, |conditional_sect| {
- ExtSubsetDeclaration::ConditionalSect(conditional_sect)
- }),
- map(DeclSep::parse, |decl_sep| {
- ExtSubsetDeclaration::DeclSep(decl_sep)
- }),
- )))(input)
+ map(
+ many0(alt((
+ map(MarkupDecl::parse, |markup_decl| {
+ ExtSubsetDeclaration::MarkupDecl(markup_decl)
+ }),
+ map(ConditionalSect::parse, |conditional_sect| {
+ ExtSubsetDeclaration::ConditionalSect(conditional_sect)
+ }),
+ map(DeclSep::parse, |decl_sep| {
+ ExtSubsetDeclaration::DeclSep(decl_sep)
+ }),
+ ))),
+ |declarations| ExtSubsetDecl(declarations),
+ )(input)
}
}