From 2e0aa3e433e643b235b4dc7de806461a3625850d Mon Sep 17 00:00:00 2001
From: Chris Wong <lambda.fairy@gmail.com>
Date: Tue, 6 Oct 2015 19:14:31 +1300
Subject: [PATCH] Replace `$$` syntax with a general trait thing

---
 maud/src/lib.rs            | 26 ++++++++++++++++++++++++++
 maud_macros/src/parse.rs   | 11 +++--------
 maud_macros/src/render.rs  | 30 +++++-------------------------
 maud_macros/tests/tests.rs |  3 ++-
 4 files changed, 36 insertions(+), 34 deletions(-)

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>");
     }