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 rust: nightly
sudo: false sudo: false
script: script:
- ( cd maud && cargo test --verbose )
- ( cd maud_macros && cargo test --verbose ) - ( cd maud_macros && cargo test --verbose )

View file

@ -8,6 +8,51 @@
use std::fmt; use std::fmt;
use std::io; 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`. /// Wraps a `std::io::Write` in a `std::fmt::Write`.
/// ///
/// Most I/O libraries work with binary data (`[u8]`), but Maud outputs /// 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<()>) { pub fn into_inner(self) -> (W, io::Result<()>) {
let Utf8Writer { inner, result } = self; let Utf8Writer { inner, result } = self;
(inner, result) (inner, result)
} }
/// Drops the inner writer, returning any errors encountered
/// along the way.
pub fn into_result(self) -> io::Result<()> { pub fn into_result(self) -> io::Result<()> {
self.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::ast::{Expr, Ident, Pat, Stmt, TokenTree, TtToken};
use syntax::codemap::DUMMY_SP; use syntax::codemap::DUMMY_SP;
use syntax::ext::base::ExtCtxt; use syntax::ext::base::ExtCtxt;
use syntax::parse::token; use syntax::parse::token;
use syntax::ptr::P; use syntax::ptr::P;
use maud; use maud::Escaper;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum Escape { pub enum Escape {
@ -119,7 +120,7 @@ impl<'cx> Renderer<'cx> {
let escaped; let escaped;
let s = match escape { let s = match escape {
Escape::PassThru => s, Escape::PassThru => s,
Escape::Escape => { escaped = maud::escape(s); &*escaped }, Escape::Escape => { escaped = html_escape(s); &*escaped },
}; };
self.push_str(s); self.push_str(s);
} }
@ -133,7 +134,7 @@ impl<'cx> Renderer<'cx> {
Escape::Escape => Escape::Escape =>
quote_expr!(self.cx, quote_expr!(self.cx,
write!( write!(
::maud::rt::Escaper { inner: $w }, ::maud::Escaper::new($w),
"{}", "{}",
$expr)), $expr)),
}; };
@ -190,3 +191,10 @@ impl<'cx> Renderer<'cx> {
self.push(stmt); 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
}