Expose Escaper type

This commit is contained in:
Chris Wong 2015-09-12 18:05:58 +12:00
parent 6030eed9f7
commit c68f98d3a7
3 changed files with 61 additions and 38 deletions
.travis.yml
maud/src
maud_macros/src

View file

@ -2,4 +2,5 @@ language: rust
rust: nightly
sudo: false
script:
- ( cd maud && cargo test --verbose )
- ( cd maud_macros && cargo test --verbose )

View file

@ -8,6 +8,51 @@
use std::fmt;
use std::io;
/// An adapter that escapes HTML special characters.
///
/// # Example
///
/// ```
/// # use maud::Escaper;
/// use std::fmt::Write;
/// let mut result = String::new();
/// write!(Escaper::new(&mut result), "<script>launchMissiles()</script>").unwrap();
/// assert_eq!(result, "&lt;script&gt;launchMissiles()&lt;/script&gt;");
/// ```
pub struct Escaper<'a> {
// FIXME: store the writer directly instead of borrowing it
// see <https://github.com/rust-lang/rust/pull/28368>
inner: &'a mut fmt::Write,
}
impl<'a> Escaper<'a> {
/// Creates an `Escaper` from a `std::fmt::Write`.
pub fn new(inner: &'a mut fmt::Write) -> Escaper<'a> {
Escaper { inner: inner }
}
/// Extracts the inner writer.
pub fn into_inner(self) -> &'a mut fmt::Write {
self.inner
}
}
impl<'a> fmt::Write for Escaper<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
try!(match c {
'&' => self.inner.write_str("&amp;"),
'<' => self.inner.write_str("&lt;"),
'>' => self.inner.write_str("&gt;"),
'"' => self.inner.write_str("&quot;"),
'\'' => self.inner.write_str("&#39;"),
_ => self.inner.write_char(c),
});
}
Ok(())
}
}
/// Wraps a `std::io::Write` in a `std::fmt::Write`.
///
/// Most I/O libraries work with binary data (`[u8]`), but Maud outputs
@ -37,11 +82,15 @@ impl<W: io::Write> Utf8Writer<W> {
}
}
/// Extracts the inner writer, along with any errors encountered
/// along the way.
pub fn into_inner(self) -> (W, io::Result<()>) {
let Utf8Writer { inner, result } = self;
(inner, result)
}
/// Drops the inner writer, returning any errors encountered
/// along the way.
pub fn into_result(self) -> io::Result<()> {
self.result
}
@ -68,38 +117,3 @@ impl<W: io::Write> fmt::Write for Utf8Writer<W> {
}
}
}
/// Escapes an HTML value.
pub fn escape(s: &str) -> String {
use std::fmt::Write;
let mut buf = String::new();
rt::Escaper { inner: &mut buf }.write_str(s).unwrap();
buf
}
/// Internal functions used by the `maud_macros` package. You should
/// never need to call these directly.
#[doc(hidden)]
pub mod rt {
use std::fmt;
pub struct Escaper<'a, 'b: 'a> {
pub inner: &'a mut (fmt::Write + 'b),
}
impl<'a, 'b> fmt::Write for Escaper<'a, 'b> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
try!(match c {
'&' => self.inner.write_str("&amp;"),
'<' => self.inner.write_str("&lt;"),
'>' => self.inner.write_str("&gt;"),
'"' => self.inner.write_str("&quot;"),
'\'' => self.inner.write_str("&#39;"),
_ => write!(self.inner, "{}", c),
});
}
Ok(())
}
}
}

View file

@ -1,10 +1,11 @@
use std::fmt::Write;
use syntax::ast::{Expr, Ident, Pat, Stmt, TokenTree, TtToken};
use syntax::codemap::DUMMY_SP;
use syntax::ext::base::ExtCtxt;
use syntax::parse::token;
use syntax::ptr::P;
use maud;
use maud::Escaper;
#[derive(Copy, Clone)]
pub enum Escape {
@ -119,7 +120,7 @@ impl<'cx> Renderer<'cx> {
let escaped;
let s = match escape {
Escape::PassThru => s,
Escape::Escape => { escaped = maud::escape(s); &*escaped },
Escape::Escape => { escaped = html_escape(s); &*escaped },
};
self.push_str(s);
}
@ -133,7 +134,7 @@ impl<'cx> Renderer<'cx> {
Escape::Escape =>
quote_expr!(self.cx,
write!(
::maud::rt::Escaper { inner: $w },
::maud::Escaper::new($w),
"{}",
$expr)),
};
@ -190,3 +191,10 @@ impl<'cx> Renderer<'cx> {
self.push(stmt);
}
}
fn html_escape(s: &str) -> String {
use std::fmt::Write;
let mut out = String::new();
Escaper::new(&mut out).write_str(s).unwrap();
out
}