#[derive(Debug, Clone)]
pub struct XmlLog {
out: String,
pub task_id: u64,
}
impl XmlLog {
pub fn new(task_id: u64) -> XmlLog {
XmlLog { out: "<Log>".to_string(), task_id }
}
pub fn element(&mut self, name: &str, attrs: &[(&str, &str)], text: Option<&str>) {
self.out.push_str("\n <");
self.out.push_str(name);
for (k, v) in attrs {
self.out.push(' ');
self.out.push_str(k);
self.out.push_str("=\"");
escape_attr(&mut self.out, v);
self.out.push('"');
}
match text {
Some(t) if !t.is_empty() => {
self.out.push('>');
escape_text(&mut self.out, t);
self.out.push_str("</");
self.out.push_str(name);
self.out.push('>');
}
_ => self.out.push_str(" />"),
}
}
pub fn finish(mut self) -> String {
self.out.push_str("\n</Log>");
self.out
}
}
fn escape_attr(out: &mut String, s: &str) {
for c in s.chars() {
match c {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
'"' => out.push_str("""),
'\n' => out.push_str("
"),
'\r' => out.push_str("
"),
'\t' => out.push_str("	"),
c => out.push(c),
}
}
}
fn escape_text(out: &mut String, s: &str) {
for c in s.chars() {
match c {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
c => out.push(c),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn writes_like_xml_writer() {
let mut x = XmlLog::new(2);
x.element("State", &[("id", "Main(1)"), ("state", "Init"), ("isEntry", "True")], None);
x.element("AssertionFailure", &[], Some("<ErrorLog> a & \"b\""));
x.element("Strategy", &[("strategy", "random")], Some("random[seed '7']"));
assert_eq!(
x.finish(),
"<Log>\n <State id=\"Main(1)\" state=\"Init\" isEntry=\"True\" />\n \
<AssertionFailure><ErrorLog> a & \"b\"</AssertionFailure>\n \
<Strategy strategy=\"random\">random[seed '7']</Strategy>\n</Log>"
);
}
}