From 663247aef3618c3e35e6cf7d4f4f59bdc7ec729a Mon Sep 17 00:00:00 2001 From: Chris Wong <lambda.fairy@gmail.com> Date: Mon, 12 Jan 2015 15:52:11 +1300 Subject: [PATCH] Use an opaque Markup type instead of a bare closure --- maud/src/lib.rs | 58 ++++++++++++++++++++++++++++++-------- maud_macros/src/render.rs | 9 +++--- maud_macros/tests/tests.rs | 24 +++++++--------- 3 files changed, 62 insertions(+), 29 deletions(-) diff --git a/maud/src/lib.rs b/maud/src/lib.rs index 7fdd9dc..54b79a8 100644 --- a/maud/src/lib.rs +++ b/maud/src/lib.rs @@ -4,12 +4,52 @@ use std::fmt; use std::fmt::Writer as FmtWriter; +use std::io::{IoError, IoErrorKind, IoResult}; pub type FmtResult<T> = Result<T, fmt::Error>; /// Escape an HTML value. pub fn escape(s: &str) -> String { - render(|w| rt::escape(w, |w| w.write_str(s))) + let mut buf = String::new(); + rt::escape(&mut buf, |w| w.write_str(s)).unwrap(); + buf +} + +/// A block of HTML markup, as returned by the `html!` macro. +pub struct Markup<'a, 'b: 'a> { + callback: &'a (Fn(&mut FmtWriter) -> FmtResult<()> + 'b), +} + +impl<'a, 'b> Markup<'a, 'b> { + /// Render the markup to a `String`. + pub fn render(&self) -> String { + let mut buf = String::new(); + self.render_fmt(&mut buf).unwrap(); + buf + } + + /// Render the markup to a `std::io::Writer`. + pub fn render_to(&self, w: &mut Writer) -> IoResult<()> { + struct WriterWrapper<'a, 'b: 'a> { + inner: &'a mut (Writer + 'b), + } + impl<'a, 'b> FmtWriter for WriterWrapper<'a, 'b> { + fn write_str(&mut self, s: &str) -> FmtResult<()> { + self.inner.write_str(s).map_err(|_| fmt::Error) + } + } + self.render_fmt(&mut WriterWrapper { inner: w }) + .map_err(|_| IoError { + kind: IoErrorKind::OtherIoError, + desc: "formatting error", + detail: None, + }) + } + + /// Render the markup to a `std::fmt::Writer`. + pub fn render_fmt(&self, w: &mut FmtWriter) -> FmtResult<()> { + (self.callback)(w) + } } /// Internal functions used by the `maud_macros` package. You should @@ -18,7 +58,12 @@ pub fn escape(s: &str) -> String { Use the macros in `maud_macros` instead."] pub mod rt { use std::fmt::Writer as FmtWriter; - use super::FmtResult; + use super::{FmtResult, Markup}; + + #[inline] + pub fn make_markup<'a, 'b>(f: &'a (Fn(&mut FmtWriter) -> FmtResult<()> + 'b)) -> Markup<'a, 'b> { + Markup { callback: f } + } struct Escaper<'a, 'b: 'a> { inner: &'a mut (FmtWriter + 'b), @@ -47,12 +92,3 @@ pub mod rt { f(&mut Escaper { inner: w }) } } - -/// Render a template into a `String`. -pub fn render<F>(template: F) -> String where - F: FnOnce(&mut FmtWriter) -> FmtResult<()> -{ - let mut buf = String::new(); - template(&mut buf).unwrap(); - buf -} diff --git a/maud_macros/src/render.rs b/maud_macros/src/render.rs index 3fc0c2e..d26d5d2 100644 --- a/maud_macros/src/render.rs +++ b/maud_macros/src/render.rs @@ -33,10 +33,11 @@ impl<'cx, 's: 'cx, 'o> Renderer<'cx, 's, 'o> { f(&mut render); render.cx }; - quote_expr!(cx, |&: $w: &mut ::std::fmt::Writer| -> ::std::result::Result<(), ::std::fmt::Error> { - $stmts - Ok(()) - }) + quote_expr!(cx, + ::maud::rt::make_markup(&|&: $w: &mut ::std::fmt::Writer| -> Result<(), ::std::fmt::Error> { + $stmts + Ok(()) + })) } /// Append a literal pre-escaped string. diff --git a/maud_macros/tests/tests.rs b/maud_macros/tests/tests.rs index 62f9e7b..07838ac 100644 --- a/maud_macros/tests/tests.rs +++ b/maud_macros/tests/tests.rs @@ -6,49 +6,45 @@ extern crate maud; #[test] fn it_works() { - let template = html!("du\tcks" -23 3.14 '\n' "geese"); - let s = maud::render(template); + let s = html!("du\tcks" -23 3.14 '\n' "geese").render(); assert_eq!(s, "du\tcks-233.14\ngeese"); } #[test] fn escaping() { - let template = html!("<flim&flam>"); - let s = maud::render(template); + let s = html!("<flim&flam>").render(); assert_eq!(s, "<flim&flam>"); } #[test] fn blocks() { - let s = maud::render(html! { + let s = html! { "hello" { " ducks"; " geese"; } " swans" - }); + }.render(); assert_eq!(s, "hello ducks geese swans"); } mod splice { - use super::maud; // lol - #[test] fn literal() { - let s = maud::render(html! { $"<pinkie>" }); + let s = html! { $"<pinkie>" }.render(); assert_eq!(s, "<pinkie>"); } #[test] fn raw_literal() { - let s = maud::render(html! { $$"<pinkie>" }); + let s = html! { $$"<pinkie>" }.render(); assert_eq!(s, "<pinkie>"); } #[test] fn block() { - let s = maud::render(html! { + let s = html! { ${ let mut result = 1i32; for i in range(2, 11) { @@ -56,7 +52,7 @@ mod splice { } result } - }); + }.render(); assert_eq!(s, "3628800"); } @@ -64,7 +60,7 @@ mod splice { #[test] fn statics() { - let s = maud::render(html! { $BEST_PONY }); + let s = html! { $BEST_PONY }.render(); assert_eq!(s, "Pinkie Pie"); } @@ -74,7 +70,7 @@ mod splice { #[test] fn closure() { let best_pony = "Pinkie Pie"; - let s = maud::render(html! { $best_pony }); + let s = html! { $best_pony }.render(); assert_eq!(s, "Pinkie Pie"); } */