diff --git a/maud/src/lib.rs b/maud/src/lib.rs index 04ba91c..b012cf4 100644 --- a/maud/src/lib.rs +++ b/maud/src/lib.rs @@ -8,6 +8,32 @@ use std::fmt; use std::io; +/// Represents a type that can be rendered as HTML. +/// +/// Most of the time you should implement `std::fmt::Display` instead, +/// which will be picked up by the blanket impl. +pub trait Render { + fn render(&self, &mut fmt::Write) -> fmt::Result; +} + +impl<T: fmt::Display + ?Sized> Render for T { + fn render(&self, w: &mut fmt::Write) -> fmt::Result { + use std::fmt::Write; + write!(Escaper::new(w), "{}", self) + } +} + +/// A wrapper that renders the inner value without escaping. +#[derive(Debug)] +pub struct PreEscaped<T>(pub T); + +impl<T: fmt::Display> Render for PreEscaped<T> { + fn render(&self, w: &mut fmt::Write) -> fmt::Result { + use std::fmt::Write; + write!(w, "{}", self.0) + } +} + /// An adapter that escapes HTML special characters. /// /// # Example diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs index 76e6b32..4d747fc 100644 --- a/maud_macros/src/parse.rs +++ b/maud_macros/src/parse.rs @@ -10,7 +10,7 @@ use syntax::parse::token::{BinOpToken, DelimToken, IdentStyle, Token}; use syntax::parse::token::keywords::Keyword; use syntax::ptr::P; -use super::render::{Escape, Renderer}; +use super::render::Renderer; macro_rules! error { ($cx:expr, $sp:expr, $msg:expr) => ({ @@ -162,15 +162,10 @@ impl<'cx, 'i> Parser<'cx, 'i> { self.render.emit_call(func); }, // Splice - [ref tt @ dollar!(), dollar!(), ..] => { - self.shift(2); - let expr = try!(self.splice(tt.get_span())); - self.render.splice(expr, Escape::PassThru); - }, [ref tt @ dollar!(), ..] => { self.shift(1); let expr = try!(self.splice(tt.get_span())); - self.render.splice(expr, Escape::Escape); + self.render.splice(expr); }, // Element [ident!(sp, _), ..] => { @@ -205,7 +200,7 @@ impl<'cx, 'i> Parser<'cx, 'i> { fn literal(&mut self, tt: &TokenTree, minus: bool) -> PResult<()> { let lit = try!(self.with_rust_parser(vec![tt.clone()], RustParser::parse_lit)); let s = try!(lit_to_string(self.render.cx, lit, minus)); - self.render.string(&s, Escape::Escape); + self.render.string(&s); Ok(()) } diff --git a/maud_macros/src/render.rs b/maud_macros/src/render.rs index 35e31e5..a2ce7fe 100644 --- a/maud_macros/src/render.rs +++ b/maud_macros/src/render.rs @@ -7,12 +7,6 @@ use syntax::ptr::P; use maud::Escaper; -#[derive(Copy, Clone)] -pub enum Escape { - PassThru, - Escape, -} - pub struct Renderer<'cx> { pub cx: &'cx ExtCtxt<'cx>, writer: Ident, @@ -116,29 +110,15 @@ impl<'cx> Renderer<'cx> { }).unwrap() } - /// Appends a literal string, with the specified escaping method. - pub fn string(&mut self, s: &str, escape: Escape) { - let escaped; - let s = match escape { - Escape::PassThru => s, - Escape::Escape => { escaped = html_escape(s); &*escaped }, - }; - self.push_str(s); + /// Appends a literal string. + pub fn string(&mut self, s: &str) { + self.push_str(&html_escape(s)); } /// Appends the result of an expression, with the specified escaping method. - pub fn splice(&mut self, expr: P<Expr>, escape: Escape) { + pub fn splice(&mut self, expr: P<Expr>) { let w = self.writer; - let expr = match escape { - Escape::PassThru => - quote_expr!(self.cx, write!($w, "{}", $expr)), - Escape::Escape => - quote_expr!(self.cx, - write!( - ::maud::Escaper::new(&mut *$w), - "{}", - $expr)), - }; + let expr = quote_expr!(self.cx, ::maud::Render::render(&$expr, &mut *$w)); let stmt = self.wrap_try(expr); self.push(stmt); } diff --git a/maud_macros/tests/tests.rs b/maud_macros/tests/tests.rs index cbbb8d6..2695e69 100644 --- a/maud_macros/tests/tests.rs +++ b/maud_macros/tests/tests.rs @@ -99,8 +99,9 @@ mod splices { #[test] fn raw_literals() { + use maud::PreEscaped; let mut s = String::new(); - html!(s, $$"<pinkie>").unwrap(); + html!(s, $PreEscaped("<pinkie>")).unwrap(); assert_eq!(s, "<pinkie>"); }