maud/maud_macros/src/render.rs
2015-02-06 16:26:35 +13:00

139 lines
3.7 KiB
Rust

use std::borrow::IntoCow;
use syntax::ast::{Expr, ExprParen, Ident, Stmt};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::ptr::P;
use maud;
#[derive(Copy)]
pub enum Escape {
PassThru,
Escape,
}
pub struct Renderer<'cx, 's: 'cx> {
pub cx: &'cx ExtCtxt<'s>,
stmts: Vec<P<Stmt>>,
w: Ident,
}
impl<'cx, 's> Renderer<'cx, 's> {
/// Create a new `Renderer` using the given extension context.
pub fn new(cx: &'cx ExtCtxt<'s>) -> Renderer<'cx, 's> {
Renderer {
cx: cx,
stmts: vec![],
w: Ident::new(token::intern("w")),
}
}
/// Create a new `Renderer` under the same context as `self`.
pub fn fork(&self) -> Renderer<'cx, 's> {
Renderer {
cx: self.cx,
stmts: vec![],
w: self.w,
}
}
/// Reify the `Renderer` into a block of markup.
pub fn into_expr(self) -> P<Expr> {
let Renderer { cx, stmts, w } = self;
quote_expr!(cx,
::maud::rt::make_markup(|&: $w: &mut ::std::fmt::Writer| -> Result<(), ::std::fmt::Error> {
$stmts
Ok(())
}))
}
/// Reify the `Renderer` into a raw list of statements.
pub fn into_stmts(self) -> Vec<P<Stmt>> {
let Renderer { stmts, .. } = self;
stmts
}
/// Push an expression statement, also wrapping it with `try!`.
fn push_try(&mut self, expr: P<Expr>) {
let stmt = self.cx.stmt_expr(self.cx.expr_try(expr.span, expr));
self.stmts.push(stmt);
}
/// Append a literal pre-escaped string.
fn write(&mut self, s: &str) {
let w = self.w;
let expr = quote_expr!(self.cx, $w.write_str($s));
self.push_try(expr);
}
/// Append a literal string, with the specified escaping method.
pub fn string(&mut self, s: &str, escape: Escape) {
let s = match escape {
Escape::PassThru => s.into_cow(),
Escape::Escape => maud::escape(s).into_cow(),
};
self.write(&s);
}
/// Append the result of an expression, with the specified escaping method.
pub fn splice(&mut self, expr: P<Expr>, escape: Escape) {
let w = self.w;
let expr = match escape {
Escape::PassThru =>
quote_expr!(self.cx, ::maud::rt::write_fmt($w, $expr)),
Escape::Escape =>
quote_expr!(self.cx,
::maud::rt::write_fmt(
&mut ::maud::rt::Escaper { inner: $w },
$expr)),
};
self.push_try(expr);
}
pub fn element_open_start(&mut self, name: &str) {
self.write("<");
self.write(name);
}
pub fn attribute_start(&mut self, name: &str) {
self.write(" ");
self.write(name);
self.write("=\"");
}
pub fn attribute_empty(&mut self, name: &str) {
self.write(" ");
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("\"");
}
pub fn element_open_end(&mut self) {
self.write(">");
}
pub fn element_close(&mut self, name: &str) {
self.write("</");
self.write(name);
self.write(">");
}
}