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 write_event<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<usize> {
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(0),
};
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<usize> {
self.writer.write(value).map_err(Error::Io)
}
#[inline]
fn write_wrapped(&mut self, before: &[u8], value: &[u8], after: &[u8]) -> Result<usize> {
let mut wrote = 0;
if let Some(ref i) = self.indent {
if i.should_line_break {
wrote = self.writer.write(b"\n").map_err(Error::Io)? + self
.writer
.write(&i.indents[..i.indents_len])
.map_err(Error::Io)?;
}
}
Ok(wrote + self.write(before)? + self.write(value)? + self.write(after)?)
}
}
#[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.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 = self.indents_len - self.indent_size;
}
}