use std::fmt::{self, Display, Formatter};
use super::*;
const INDENT: usize = 4;
struct IndentedXml<'a>(&'a Xml, usize);
struct IndentedTag<'a>(&'a Tag, usize);
struct IndentedContent<'a>(&'a Content, usize);
fn escape_attr(s: &str) -> String {
s.chars().fold(String::new(), |mut s, ch| {
match ch {
'\\' => s + "\\\\",
'\"' => s + "\\\"",
'\n' => s + "\\n",
_ => { s.push(ch); s },
}
})
}
impl Display for IndentedXml<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let Self(x, ind) = self;
let ind2 = ind + INDENT;
x.0.iter().try_for_each(|x| write!(f, "{}", IndentedContent(x, ind2)))
}
}
impl Display for IndentedTag<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let Self(tag, ind) = self;
let ind2 = ind + INDENT;
let n = &tag.name;
write!(f, "{:ind$}<{n}", "")?;
tag.attrs.iter().try_for_each(|a| write!(f, " {a}"))?;
if let Some(inner) = tag.inner.as_ref() {
match &inner.0[..] {
&[] => write!(f, "></{n}>"),
&[ref x] => {
let sep = match x {
Content::Tag(_) => true,
Content::Nested(_) => true,
Content::Word(_) => false,
};
if sep {
writeln!(f ,">")?;
writeln!(f, "{}", IndentedContent(x, ind2))?;
write!(f, "{:ind$}</{n}>", "")
} else {
write!(f, ">{x}</{n}>")
}
},
data => {
writeln!(f, ">")?;
data.iter().try_for_each(|x| writeln!(f, "{}", IndentedContent(x, ind2)))?;
write!(f, "{:ind$}</{n}>", "")
}
}
} else {
write!(f, " />")
}
}
}
impl Display for IndentedContent<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let Self(x, ind) = self;
match x {
Content::Tag(t) => write!(f, "{}", IndentedTag(t, *ind)),
Content::Word(w) => write!(f, "{:ind$}{}", "", w),
Content::Nested(x) => write!(f, "{}", IndentedXml(x, *ind - INDENT)),
}
}
}
impl Display for Document {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.xml_decl_attrs.len() > 0 {
write!(f, "<?xml")?;
for (n, v) in &self.xml_decl_attrs {
write!(f, " {}=\"{}\"", n, escape_attr(v))?;
}
writeln!(f, "?>")?;
}
if let Some(doctype) = &self.doctype {
writeln!(f, "<!DOCTYPE {doctype}>")?;
}
write!(f, "{}", self.root)
}
}
impl Display for Xml {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.iter().try_for_each(|x| write!(f, "{x}"))
}
}
impl Display for Tag {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", IndentedTag(self, 0))
}
}
impl Display for Attr {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}=\"{}\"", self.name, escape_attr(&self.value))
}
}
impl Display for Content {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Tag(t) => write!(f, "{t}"),
Self::Word(w) => write!(f, "{w}"),
Self::Nested(x) => write!(f, "{}", IndentedXml(x, 0)),
}
}
}