Don't use different escaping rules for attributes

This commit is contained in:
Chris Wong 2015-01-11 10:58:27 +13:00
parent 0b531572cf
commit b1aa300884
3 changed files with 20 additions and 78 deletions
maud/src
maud_macros/src

View file

@ -7,27 +7,9 @@ use std::fmt::Writer as FmtWriter;
pub type FmtResult<T> = Result<T, fmt::Error>;
/// Utilities for escaping HTML5 markup.
///
/// These follow the *HTML fragment serialization algorithm*, as
/// specified by the [HTML 5.1 Working Draft][1].
///
/// [1]: http://www.w3.org/TR/html51/syntax.html#escapingString
pub mod escape {
use std::fmt::Writer as FmtWriter;
use super::render;
use super::rt;
/// Escape a double-quoted attribute value, as per HTML5 rules.
pub fn attribute(s: &str) -> String {
render(|w| rt::escape_attribute(w, |w| w.write_str(s)))
}
/// Escape non-attribute text content, as per HTML5 rules.
pub fn non_attribute(s: &str) -> String {
render(|w| rt::escape_non_attribute(w, |w| w.write_str(s)))
}
/// Escape an HTML value.
pub fn escape(s: &str) -> String {
render(|w| rt::escape(w, |w| w.write_str(s)))
}
/// Internal functions used by the `maud_macros` package. You should
@ -38,36 +20,19 @@ pub mod rt {
use std::fmt::Writer as FmtWriter;
use super::FmtResult;
struct AttrEscaper<'a, 'b: 'a> {
struct Escaper<'a, 'b: 'a> {
inner: &'a mut (FmtWriter + 'b),
}
impl<'a, 'b> FmtWriter for AttrEscaper<'a, 'b> {
impl<'a, 'b> FmtWriter for Escaper<'a, 'b> {
fn write_str(&mut self, s: &str) -> FmtResult<()> {
for c in s.chars() {
try!(match c {
'&' => self.inner.write_str("&amp;"),
'\u{A0}' => self.inner.write_str("&nbsp;"),
'"' => self.inner.write_str("&quot;"),
_ => write!(self.inner, "{}", c),
});
}
Ok(())
}
}
struct NonAttrEscaper<'a, 'b: 'a> {
inner: &'a mut (FmtWriter + 'b),
}
impl<'a, 'b> FmtWriter for NonAttrEscaper<'a, 'b> {
fn write_str(&mut self, s: &str) -> FmtResult<()> {
for c in s.chars() {
try!(match c {
'&' => self.inner.write_str("&amp;"),
'\u{A0}' => self.inner.write_str("&nbsp;"),
'<' => self.inner.write_str("&lt;"),
'>' => self.inner.write_str("&gt;"),
'"' => self.inner.write_str("&quot;"),
'\'' => self.inner.write_str("&#39;"),
_ => write!(self.inner, "{}", c),
});
}
@ -76,17 +41,10 @@ pub mod rt {
}
#[inline]
pub fn escape_attribute<F>(w: &mut FmtWriter, f: F) -> FmtResult<()> where
pub fn escape<F>(w: &mut FmtWriter, f: F) -> FmtResult<()> where
F: FnOnce(&mut FmtWriter) -> FmtResult<()>
{
f(&mut AttrEscaper { inner: w })
}
#[inline]
pub fn escape_non_attribute<F>(w: &mut FmtWriter, f: F) -> FmtResult<()> where
F: FnOnce(&mut FmtWriter) -> FmtResult<()>
{
f(&mut NonAttrEscaper { inner: w })
f(&mut Escaper { inner: w })
}
}

View file

@ -9,9 +9,8 @@ use super::render::Renderer;
#[derive(Copy, PartialEq, Show)]
pub enum Escape {
None,
Attr,
Body,
PassThru,
Escape,
}
macro_rules! guard {
@ -85,14 +84,6 @@ impl<'cx: 'r, 's: 'cx, 'i, 'r, 'o: 'r> Parser<'cx, 's, 'i, 'r, 'o> {
self.input = self.input.slice_from(n);
}
fn choose_escape(&self) -> Escape {
if self.in_attr {
Escape::Attr
} else {
Escape::Body
}
}
/// Construct a Rust AST parser from the given token tree.
fn new_rust_parser(&self, tts: Vec<TokenTree>) -> RustParser<'s> {
parse::tts_to_parser(self.render.cx.parse_sess, tts, self.render.cx.cfg.clone())
@ -135,10 +126,7 @@ impl<'cx: 'r, 's: 'cx, 'i, 'r, 'o: 'r> Parser<'cx, 's, 'i, 'r, 'o> {
};
let lit = self.new_rust_parser(vec![tt.clone()]).parse_lit();
match lit_to_string(self.render.cx, lit, minus) {
Some(s) => {
let escape = self.choose_escape();
self.render.string(s.as_slice(), escape);
},
Some(s) => self.render.string(s.as_slice(), Escape::Escape),
None => return false,
}
true
@ -148,11 +136,11 @@ impl<'cx: 'r, 's: 'cx, 'i, 'r, 'o: 'r> Parser<'cx, 's, 'i, 'r, 'o> {
let (escape, sp) = match self.input {
[ref tt @ dollar!(), dollar!(), ..] => {
self.shift(2);
(Escape::None, tt.get_span())
(Escape::PassThru, tt.get_span())
},
[ref tt @ dollar!(), ..] => {
self.shift(1);
(self.choose_escape(), tt.get_span())
(Escape::Escape, tt.get_span())
},
_ => return false,
};

View file

@ -4,8 +4,8 @@ use syntax::ext::base::ExtCtxt;
use syntax::parse::token;
use syntax::ptr::P;
use maud;
use super::parse::Escape;
use maud::escape;
pub struct Renderer<'cx, 's: 'cx, 'o> {
pub cx: &'cx mut ExtCtxt<'s>,
@ -43,9 +43,8 @@ impl<'cx, 's: 'cx, 'o> Renderer<'cx, 's, 'o> {
/// Append a literal string, with the specified escaping method.
pub fn string(&mut self, s: &str, escape: Escape) {
let s = match escape {
Escape::None => s.into_cow(),
Escape::Attr => escape::attribute(s).into_cow(),
Escape::Body => escape::non_attribute(s).into_cow(),
Escape::PassThru => s.into_cow(),
Escape::Escape => maud::escape(s).into_cow(),
};
self.write(s.as_slice());
}
@ -54,13 +53,10 @@ impl<'cx, 's: 'cx, 'o> Renderer<'cx, 's, 'o> {
pub fn splice(&mut self, expr: P<Expr>, escape: Escape) {
let w = self.w;
self.stmts.push(match escape {
Escape::None => quote_stmt!(self.cx, try!(write!($w, "{}", $expr))),
Escape::Attr =>
Escape::PassThru => quote_stmt!(self.cx, try!(write!($w, "{}", $expr))),
Escape::Escape =>
quote_stmt!(self.cx,
try!(::maud::rt::escape_attribute($w, |w| write!(w, "{}", $expr)))),
Escape::Body =>
quote_stmt!(self.cx,
try!(::maud::rt::escape_non_attribute($w, |w| write!(w, "{}", $expr)))),
try!(::maud::rt::escape($w, |w| write!(w, "{}", $expr)))),
});
}