diff --git a/maud/src/lib.rs b/maud/src/lib.rs index b08d53a..d326843 100644 --- a/maud/src/lib.rs +++ b/maud/src/lib.rs @@ -5,6 +5,70 @@ //! //! [book]: http://lfairy.gitbooks.io/maud/content/ +use std::fmt; +use std::io; + +/// Wraps a `std::io::Write` in a `std::fmt::Write`. +/// +/// Most I/O libraries work with binary data (`[u8]`), but Maud outputs +/// Unicode strings (`str`). This adapter links them together by +/// encoding the output as UTF-8. +/// +/// # Example +/// +/// ```rust,ignore +/// use std::io; +/// let writer = Utf8Writer::new(io::stdout()); +/// let _ = html!(writer, p { "Hello, " $name "!" }); +/// let result = writer.into_result(); +/// result.unwrap(); +/// ``` +pub struct Utf8Writer<W: io::Write> { + inner: W, + result: io::Result<()>, +} + +impl<W: io::Write> Utf8Writer<W> { + /// Creates a `Utf8Writer` from a `std::io::Write`. + pub fn new(inner: W) -> Utf8Writer<W> { + Utf8Writer { + inner: inner, + result: Ok(()), + } + } + + pub fn into_inner(self) -> (W, io::Result<()>) { + let Utf8Writer { inner, result } = self; + (inner, result) + } + + pub fn into_result(self) -> io::Result<()> { + self.result + } +} + +impl<W: io::Write> fmt::Write for Utf8Writer<W> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match io::Write::write_all(&mut self.inner, s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.result = Err(e); + Err(fmt::Error) + } + } + } + + fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { + match io::Write::write_fmt(&mut self.inner, args) { + Ok(()) => Ok(()), + Err(e) => { + self.result = Err(e); + Err(fmt::Error) + } + } + } +} + /// Escapes an HTML value. pub fn escape(s: &str) -> String { use std::fmt::Write; diff --git a/maud_macros/tests/tests.rs b/maud_macros/tests/tests.rs index 24011b7..a1f0eb1 100644 --- a/maud_macros/tests/tests.rs +++ b/maud_macros/tests/tests.rs @@ -248,3 +248,13 @@ mod control { "</ul>")); } } + +#[test] +fn utf8_writer() { + use maud::Utf8Writer; + let mut w = Utf8Writer::new(vec![]); + let _ = html!(w, p "hello"); + let (buf, r) = w.into_inner(); + r.unwrap(); + assert_eq!(buf, b"<p>hello</p>"); +}