use std::io::Write;
use errors::{Error, Result};
use events::Event;
#[derive(Clone)]
pub struct Writer<W: Write> {
writer: W,
indent: Option<Indentation>,
}
impl<W: Write> Writer<W> {
pub fn new(inner: W) -> Writer<W> {
Writer {
writer: inner,
indent: None,
}
}
pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
Writer {
writer: inner,
indent: Some(Indentation::new(indent_char, indent_size)),
}
}
pub fn into_inner(self) -> W {
self.writer
}
pub fn inner(&mut self) -> &mut W {
&mut self.writer
}
pub fn write_event<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<()> {
let mut next_should_line_break = true;
let result = match *event.as_ref() {
Event::Start(ref e) => {
let result = self.write_wrapped(b"<", e, b">");
if let Some(i) = self.indent.as_mut() {
i.grow();
}
result
}
Event::End(ref e) => {
if let Some(i) = self.indent.as_mut() {
i.shrink();
}
self.write_wrapped(b"</", e, b">")
}
Event::Empty(ref e) => self.write_wrapped(b"<", e, b"/>"),
Event::Text(ref e) => {
next_should_line_break = false;
self.write(&e.escaped())
}
Event::Comment(ref e) => self.write_wrapped(b"<!--", e, b"-->"),
Event::CData(ref e) => self.write_wrapped(b"<![CDATA[", e, b"]]>"),
Event::Decl(ref e) => self.write_wrapped(b"<?", e, b"?>"),
Event::PI(ref e) => self.write_wrapped(b"<?", e, b"?>"),
Event::DocType(ref e) => self.write_wrapped(b"<!DOCTYPE", e, b">"),
Event::Eof => Ok(()),
};
if let Some(i) = self.indent.as_mut() {
i.should_line_break = next_should_line_break;
}
result
}
#[inline]
pub fn write(&mut self, value: &[u8]) -> Result<()> {
self.writer.write_all(value).map_err(Error::Io)
}
#[inline]
fn write_wrapped(&mut self, before: &[u8], value: &[u8], after: &[u8]) -> Result<()> {
if let Some(ref i) = self.indent {
if i.should_line_break {
self.writer.write_all(b"\n").map_err(Error::Io)?;
self.writer
.write_all(&i.indents[..i.indents_len])
.map_err(Error::Io)?;
}
}
self.write(before)?;
self.write(value)?;
self.write(after)?;
Ok(())
}
pub fn write_indent(&mut self) -> Result<()> {
if let Some(ref i) = self.indent {
self.writer.write_all(b"\n").map_err(Error::Io)?;
self.writer
.write_all(&i.indents[..i.indents_len])
.map_err(Error::Io)?;
}
Ok(())
}
}
#[derive(Clone)]
struct Indentation {
should_line_break: bool,
indent_char: u8,
indent_size: usize,
indents: Vec<u8>,
indents_len: usize,
}
impl Indentation {
fn new(indent_char: u8, indent_size: usize) -> Indentation {
Indentation {
should_line_break: false,
indent_char,
indent_size,
indents: vec![indent_char; 128],
indents_len: 0,
}
}
fn grow(&mut self) {
self.indents_len += self.indent_size;
if self.indents_len > self.indents.len() {
self.indents.resize(self.indents_len, self.indent_char);
}
}
fn shrink(&mut self) {
self.indents_len = match self.indents_len.checked_sub(self.indent_size) {
Some(result) => result,
None => 0,
};
}
}
#[cfg(test)]
mod indentation {
use super::*;
use events::*;
#[test]
fn self_closed() {
let mut buffer = Vec::new();
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
let tag = BytesStart::borrowed_name(b"self-closed").with_attributes(vec![
("attr1", "value1"),
("attr2", "value2"),
].into_iter());
writer.write_event(Event::Empty(tag)).expect("write tag failed");
assert_eq!(buffer, br#"<self-closed attr1="value1" attr2="value2"/>"#.as_ref());
}
#[test]
fn empty_paired() {
let mut buffer = Vec::new();
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
let name = b"paired";
let start = BytesStart::borrowed_name(name).with_attributes(vec![
("attr1", "value1"),
("attr2", "value2"),
].into_iter());
let end = BytesEnd::borrowed(name);
writer.write_event(Event::Start(start)).expect("write start tag failed");
writer.write_event(Event::End(end)).expect("write end tag failed");
assert_eq!(buffer, br#"<paired attr1="value1" attr2="value2">
</paired>"#.as_ref());
}
#[test]
fn paired_with_inner() {
let mut buffer = Vec::new();
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
let name = b"paired";
let start = BytesStart::borrowed_name(name).with_attributes(vec![
("attr1", "value1"),
("attr2", "value2"),
].into_iter());
let end = BytesEnd::borrowed(name);
let inner = BytesStart::borrowed_name(b"inner");
writer.write_event(Event::Start(start)).expect("write start tag failed");
writer.write_event(Event::Empty(inner)).expect("write inner tag failed");
writer.write_event(Event::End(end)).expect("write end tag failed");
assert_eq!(buffer, br#"<paired attr1="value1" attr2="value2">
<inner/>
</paired>"#.as_ref());
}
#[test]
fn paired_with_text() {
let mut buffer = Vec::new();
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
let name = b"paired";
let start = BytesStart::borrowed_name(name).with_attributes(vec![
("attr1", "value1"),
("attr2", "value2"),
].into_iter());
let end = BytesEnd::borrowed(name);
let text = BytesText::from_plain(b"text");
writer.write_event(Event::Start(start)).expect("write start tag failed");
writer.write_event(Event::Text(text)).expect("write text failed");
writer.write_event(Event::End(end)).expect("write end tag failed");
assert_eq!(buffer, br#"<paired attr1="value1" attr2="value2">text</paired>"#.as_ref());
}
#[test]
fn mixed_content() {
let mut buffer = Vec::new();
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
let name = b"paired";
let start = BytesStart::borrowed_name(name).with_attributes(vec![
("attr1", "value1"),
("attr2", "value2"),
].into_iter());
let end = BytesEnd::borrowed(name);
let text = BytesText::from_plain(b"text");
let inner = BytesStart::borrowed_name(b"inner");
writer.write_event(Event::Start(start)).expect("write start tag failed");
writer.write_event(Event::Text(text)).expect("write text failed");
writer.write_event(Event::Empty(inner)).expect("write inner tag failed");
writer.write_event(Event::End(end)).expect("write end tag failed");
assert_eq!(buffer, br#"<paired attr1="value1" attr2="value2">text<inner/>
</paired>"#.as_ref());
}
#[test]
fn nested() {
let mut buffer = Vec::new();
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
let name = b"paired";
let start = BytesStart::borrowed_name(name).with_attributes(vec![
("attr1", "value1"),
("attr2", "value2"),
].into_iter());
let end = BytesEnd::borrowed(name);
let inner = BytesStart::borrowed_name(b"inner");
writer.write_event(Event::Start(start.clone())).expect("write start 1 tag failed");
writer.write_event(Event::Start(start)).expect("write start 2 tag failed");
writer.write_event(Event::Empty(inner)).expect("write inner tag failed");
writer.write_event(Event::End(end.clone())).expect("write end tag 2 failed");
writer.write_event(Event::End(end)).expect("write end tag 1 failed");
assert_eq!(buffer, br#"<paired attr1="value1" attr2="value2">
<paired attr1="value1" attr2="value2">
<inner/>
</paired>
</paired>"#.as_ref());
}
}