#[derive(Debug, Default)]
pub struct XmlWriter {
buf: String,
}
impl XmlWriter {
#[must_use]
pub fn new() -> Self {
Self::with_capacity(512)
}
#[must_use]
pub fn with_capacity(cap: usize) -> Self {
Self {
buf: String::with_capacity(cap),
}
}
pub fn declaration(&mut self) {
self.buf
.push_str("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
}
pub fn open_root(&mut self, name: &str, namespace: Option<&str>) {
self.buf.push('<');
self.buf.push_str(name);
if let Some(ns) = namespace {
self.buf.push_str(" xmlns=\"");
self.buf.push_str(ns);
self.buf.push('\"');
}
self.buf.push('>');
}
pub fn open(&mut self, name: &str) {
self.buf.push('<');
self.buf.push_str(name);
self.buf.push('>');
}
pub fn close(&mut self, name: &str) {
self.buf.push_str("</");
self.buf.push_str(name);
self.buf.push('>');
}
pub fn empty(&mut self, name: &str) {
self.buf.push('<');
self.buf.push_str(name);
self.buf.push_str("/>");
}
pub fn element(&mut self, name: &str, text: &str) {
self.open(name);
self.buf.push_str(&escape(text));
self.close(name);
}
pub fn element_display<D: std::fmt::Display>(&mut self, name: &str, v: D) {
let s = v.to_string();
self.element(name, &s);
}
pub fn bool(&mut self, name: &str, v: bool) {
self.element(name, if v { "true" } else { "false" });
}
pub fn optional_str(&mut self, name: &str, v: &str) {
if !v.is_empty() {
self.element(name, v);
}
}
pub fn items<T, F: FnMut(&mut Self, &T)>(
&mut self,
items: &[T],
wrapper: &str,
item_name: &str,
mut emit: F,
) {
self.open(wrapper);
self.element_display("Quantity", items.len());
if items.is_empty() {
} else {
self.open("Items");
for it in items {
self.open(item_name);
emit(self, it);
self.close(item_name);
}
self.close("Items");
}
self.close(wrapper);
}
#[must_use]
pub fn finish(self) -> String {
self.buf
}
pub fn raw(&mut self, s: &str) {
self.buf.push_str(s);
}
}
fn escape(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for c in s.chars() {
match c {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
'"' => out.push_str("""),
'\'' => out.push_str("'"),
_ => out.push(c),
}
}
out
}