139 lines
3.7 KiB
Rust
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(">");
|
|
}
|
|
}
|