From c68f98d3a7131c9c83c69c22f72bea9af608e797 Mon Sep 17 00:00:00 2001 From: Chris Wong <lambda.fairy@gmail.com> Date: Sat, 12 Sep 2015 18:05:58 +1200 Subject: [PATCH] Expose `Escaper` type --- .travis.yml | 1 + maud/src/lib.rs | 84 +++++++++++++++++++++++---------------- maud_macros/src/render.rs | 14 +++++-- 3 files changed, 61 insertions(+), 38 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2cb40c2..c3cfbad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,5 @@ language: rust rust: nightly sudo: false script: + - ( cd maud && cargo test --verbose ) - ( cd maud_macros && cargo test --verbose ) diff --git a/maud/src/lib.rs b/maud/src/lib.rs index 84a7bee..d03986e 100644 --- a/maud/src/lib.rs +++ b/maud/src/lib.rs @@ -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, "<script>launchMissiles()</script>"); +/// ``` +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("&"), + '<' => self.inner.write_str("<"), + '>' => self.inner.write_str(">"), + '"' => self.inner.write_str("""), + '\'' => self.inner.write_str("'"), + _ => 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("&"), - '<' => self.inner.write_str("<"), - '>' => self.inner.write_str(">"), - '"' => self.inner.write_str("""), - '\'' => self.inner.write_str("'"), - _ => write!(self.inner, "{}", c), - }); - } - Ok(()) - } - } -} diff --git a/maud_macros/src/render.rs b/maud_macros/src/render.rs index 594fb62..f319fdf 100644 --- a/maud_macros/src/render.rs +++ b/maud_macros/src/render.rs @@ -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 +}