maud/maud_macros/src/render.rs

190 lines
5.5 KiB
Rust
Raw Normal View History

2015-09-01 18:26:50 +12:00
use syntax::ast::{Expr, Ident, Pat, Stmt, TokenTree, TtToken};
use syntax::codemap::DUMMY_SP;
2014-12-18 18:57:55 +13:00
use syntax::ext::base::ExtCtxt;
use syntax::parse::token;
use syntax::ptr::P;
use maud;
2015-01-11 11:01:39 +13:00
2015-04-04 10:34:31 +13:00
#[derive(Copy, Clone)]
2015-01-11 11:01:39 +13:00
pub enum Escape {
PassThru,
Escape,
}
2014-12-18 18:57:55 +13:00
2015-04-04 10:27:44 +13:00
pub struct Renderer<'cx> {
pub cx: &'cx ExtCtxt<'cx>,
2015-09-01 18:56:02 +12:00
writer: Ident,
result: Ident,
2015-09-01 18:26:50 +12:00
loop_label: Vec<TokenTree>,
2015-04-10 18:44:59 +12:00
stmts: Vec<P<Stmt>>,
tail: String,
2014-12-18 18:57:55 +13:00
}
2015-04-04 10:27:44 +13:00
impl<'cx> Renderer<'cx> {
/// Creates a new `Renderer` using the given extension context.
pub fn new(cx: &'cx ExtCtxt<'cx>) -> Renderer<'cx> {
2015-09-01 18:56:02 +12:00
let writer = token::gensym_ident("__maud_writer");
let result = token::gensym_ident("__maud_result");
2015-09-01 18:26:50 +12:00
let loop_label = token::gensym_ident("__maud_loop_label");
Renderer {
cx: cx,
2015-09-01 18:56:02 +12:00
writer: writer,
result: result,
2015-09-01 18:26:50 +12:00
loop_label: vec![TtToken(DUMMY_SP, token::Lifetime(loop_label))],
stmts: vec![],
2015-04-10 18:44:59 +12:00
tail: String::new(),
}
}
2015-04-04 10:27:44 +13:00
/// Creates a new `Renderer` under the same context as `self`.
pub fn fork(&self) -> Renderer<'cx> {
2015-02-06 15:43:53 +13:00
Renderer {
cx: self.cx,
2015-09-01 18:56:02 +12:00
writer: self.writer,
result: self.result,
2015-09-01 18:26:50 +12:00
loop_label: self.loop_label.clone(),
2015-04-10 18:44:59 +12:00
stmts: Vec::new(),
tail: String::new(),
}
}
/// Flushes the tail buffer, emitting a single `.write_str()` call.
fn flush(&mut self) {
if !self.tail.is_empty() {
let expr = {
2015-09-01 18:56:02 +12:00
let w = self.writer;
2015-04-10 18:44:59 +12:00
let s = &*self.tail;
quote_expr!(self.cx, $w.write_str($s))
};
2015-09-01 18:26:50 +12:00
let stmt = self.wrap_try(expr);
2015-04-10 18:44:59 +12:00
self.stmts.push(stmt);
self.tail.clear();
2015-02-06 15:43:53 +13:00
}
}
2015-04-08 22:51:21 +12:00
/// Reifies the `Renderer` into a block of markup.
pub fn into_expr(mut self, writer_expr: Vec<TokenTree>) -> P<Expr> {
let Renderer { cx, writer, result, loop_label, stmts, .. } = { self.flush(); self };
2015-09-01 18:26:50 +12:00
quote_expr!(cx, {
2015-09-01 18:56:02 +12:00
let mut $result = Ok(());
2015-09-01 18:26:50 +12:00
$loop_label: loop {
use ::std::fmt::Write;
match &mut $writer_expr {
$writer => { $stmts }
}
2015-09-01 18:26:50 +12:00
break $loop_label;
}
2015-09-01 18:56:02 +12:00
$result
2015-09-01 18:26:50 +12:00
})
2014-12-19 11:53:40 +13:00
}
2015-04-08 22:51:21 +12:00
/// Reifies the `Renderer` into a raw list of statements.
2015-04-10 18:44:59 +12:00
pub fn into_stmts(mut self) -> Vec<P<Stmt>> {
let Renderer { stmts, .. } = { self.flush(); self };
2015-02-06 15:43:53 +13:00
stmts
}
2015-04-10 20:21:09 +12:00
/// Pushes a statement, flushing the tail buffer in the process.
2015-04-10 18:44:59 +12:00
fn push(&mut self, stmt: P<Stmt>) {
self.flush();
self.stmts.push(stmt);
2015-01-17 20:43:51 +13:00
}
2015-04-10 18:44:59 +12:00
/// Pushes a literal string to the tail buffer.
fn push_str(&mut self, s: &str) {
self.tail.push_str(s);
}
2015-09-01 18:26:50 +12:00
/// Wraps an expression in a `try!` call.
fn wrap_try(&self, expr: P<Expr>) -> P<Stmt> {
2015-09-01 18:56:02 +12:00
let result = self.result;
2015-09-01 18:26:50 +12:00
let loop_label = &self.loop_label;
quote_stmt!(
self.cx,
match $expr {
Ok(()) => {},
Err(e) => {
2015-09-01 18:56:02 +12:00
$result = Err(e);
2015-09-01 18:26:50 +12:00
break $loop_label;
}
}).unwrap()
}
2015-04-08 22:51:21 +12:00
/// Appends a literal string, with the specified escaping method.
pub fn string(&mut self, s: &str, escape: Escape) {
2015-02-27 17:25:27 +13:00
let escaped;
let s = match escape {
2015-02-27 17:25:27 +13:00
Escape::PassThru => s,
Escape::Escape => { escaped = maud::escape(s); &*escaped },
};
2015-04-10 18:44:59 +12:00
self.push_str(s);
}
2015-04-08 22:51:21 +12:00
/// Appends the result of an expression, with the specified escaping method.
pub fn splice(&mut self, expr: P<Expr>, escape: Escape) {
2015-09-01 18:56:02 +12:00
let w = self.writer;
2015-01-17 20:43:51 +13:00
let expr = match escape {
Escape::PassThru =>
quote_expr!(self.cx, write!($w, "{}", $expr)),
Escape::Escape =>
2015-01-17 20:43:51 +13:00
quote_expr!(self.cx,
write!(
::maud::rt::Escaper { inner: $w },
"{}",
$expr)),
2015-01-17 20:43:51 +13:00
};
2015-09-01 18:26:50 +12:00
let stmt = self.wrap_try(expr);
2015-04-10 18:44:59 +12:00
self.push(stmt);
}
pub fn element_open_start(&mut self, name: &str) {
2015-04-10 18:44:59 +12:00
self.push_str("<");
self.push_str(name);
}
pub fn attribute_start(&mut self, name: &str) {
2015-04-10 18:44:59 +12:00
self.push_str(" ");
self.push_str(name);
self.push_str("=\"");
}
2015-01-13 16:46:37 +13:00
pub fn attribute_empty(&mut self, name: &str) {
2015-04-10 18:44:59 +12:00
self.push_str(" ");
self.push_str(name);
2015-01-13 16:46:37 +13:00
}
pub fn attribute_end(&mut self) {
2015-04-10 18:44:59 +12:00
self.push_str("\"");
}
pub fn element_open_end(&mut self) {
2015-04-10 18:44:59 +12:00
self.push_str(">");
}
pub fn element_close(&mut self, name: &str) {
2015-04-10 18:44:59 +12:00
self.push_str("</");
self.push_str(name);
self.push_str(">");
2014-12-18 18:57:55 +13:00
}
2015-02-09 15:05:50 +13:00
2015-04-08 22:51:21 +12:00
/// Emits an `if` expression.
2015-03-15 16:23:19 +13:00
///
/// The condition is a token tree (not an expression) so we don't
/// need to special-case `if let`.
pub fn emit_if(&mut self, if_cond: Vec<TokenTree>, if_body: Vec<P<Stmt>>,
2015-02-09 15:05:50 +13:00
else_body: Option<Vec<P<Stmt>>>) {
let stmt = match else_body {
2015-03-01 19:34:08 -05:00
None => quote_stmt!(self.cx, if $if_cond { $if_body }),
2015-02-09 15:05:50 +13:00
Some(else_body) =>
2015-03-01 19:34:08 -05:00
quote_stmt!(self.cx, if $if_cond { $if_body } else { $else_body }),
2015-03-21 13:38:39 +13:00
}.unwrap();
2015-04-10 18:44:59 +12:00
self.push(stmt);
2015-02-09 15:05:50 +13:00
}
2015-03-14 21:08:08 +13:00
pub fn emit_for(&mut self, pattern: P<Pat>, iterable: P<Expr>, body: Vec<P<Stmt>>) {
2015-03-21 13:38:39 +13:00
let stmt = quote_stmt!(self.cx, for $pattern in $iterable { $body }).unwrap();
2015-04-10 18:44:59 +12:00
self.push(stmt);
2015-03-14 21:08:08 +13:00
}
2014-12-18 18:57:55 +13:00
}