From fa9404872efa17012f1991c1865d3db75debeda7 Mon Sep 17 00:00:00 2001
From: Chris Wong <lambda.fairy@gmail.com>
Date: Mon, 9 Feb 2015 15:05:50 +1300
Subject: [PATCH] Refactor toggleable attributes

---
 maud_macros/src/parse.rs  | 19 +++++++++++++++++--
 maud_macros/src/render.rs | 28 +++++++++++-----------------
 2 files changed, 28 insertions(+), 19 deletions(-)

diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs
index aaa2a2a..84d674e 100644
--- a/maud_macros/src/parse.rs
+++ b/maud_macros/src/parse.rs
@@ -1,4 +1,4 @@
-use syntax::ast::{Expr, Lit, Stmt, TokenTree, TtDelimited, TtToken};
+use syntax::ast::{Expr, ExprParen, Lit, Stmt, TokenTree, TtDelimited, TtToken};
 use syntax::codemap::Span;
 use syntax::ext::base::ExtCtxt;
 use syntax::parse;
@@ -207,7 +207,14 @@ impl<'cx, 's, 'i> Parser<'cx, 's, 'i> {
                     // Toggle the attribute based on a boolean expression
                     self.shift(1);
                     let cond = self.splice(tt.get_span());
-                    self.render.attribute_empty_if(name.as_str(), cond);
+                    // Silence "unnecessary parentheses" warnings
+                    let cond = strip_outer_parens(cond);
+                    let body = {
+                        let mut r = self.render.fork();
+                        r.attribute_empty(name.as_str());
+                        r.into_stmts()
+                    };
+                    self.render.emit_if(cond, body, None);
                 } else {
                     // Write the attribute unconditionally
                     self.render.attribute_empty(name.as_str());
@@ -249,3 +256,11 @@ fn lit_to_string(cx: &ExtCtxt, lit: Lit, minus: bool) -> Option<String> {
     };
     Some(result)
 }
+
+/// If the expression is wrapped in parentheses, strip them off.
+fn strip_outer_parens(expr: P<Expr>) -> P<Expr> {
+    expr.and_then(|expr| match expr {
+        Expr { node: ExprParen(inner), .. } => inner,
+        expr => P(expr),
+    })
+}
diff --git a/maud_macros/src/render.rs b/maud_macros/src/render.rs
index dc87d24..9b3a97c 100644
--- a/maud_macros/src/render.rs
+++ b/maud_macros/src/render.rs
@@ -1,5 +1,5 @@
 use std::borrow::IntoCow;
-use syntax::ast::{Expr, ExprParen, Ident, Stmt};
+use syntax::ast::{Expr, Ident, Stmt};
 use syntax::ext::base::ExtCtxt;
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token;
@@ -112,22 +112,6 @@ impl<'cx, 's> Renderer<'cx, 's> {
         self.write(name);
     }
 
-    pub fn attribute_empty_if(&mut self, name: &str, cond: P<Expr>) {
-        // Silence "unnecessary parentheses" warnings
-        let cond = match cond.node {
-            ExprParen(ref inner) => inner.clone(),
-            _ => cond.clone(),
-        };
-        let body = {
-            let mut r = self.fork();
-            r.write(" ");
-            r.write(name);
-            r.into_stmts()
-        };
-        let stmt = quote_stmt!(self.cx, if $cond { $body });
-        self.stmts.push(stmt);
-    }
-
     pub fn attribute_end(&mut self) {
         self.write("\"");
     }
@@ -141,4 +125,14 @@ impl<'cx, 's> Renderer<'cx, 's> {
         self.write(name);
         self.write(">");
     }
+
+    pub fn emit_if(&mut self, cond: P<Expr>, if_body: Vec<P<Stmt>>,
+                   else_body: Option<Vec<P<Stmt>>>) {
+        let stmt = match else_body {
+            None => quote_stmt!(self.cx, if $cond { $if_body }),
+            Some(else_body) =>
+                quote_stmt!(self.cx, if $cond { $if_body } else { $else_body }),
+        };
+        self.stmts.push(stmt);
+    }
 }