Move escaping routines into a shared maud_htmlescape crate

This commit is contained in:
Chris Wong 2017-07-29 19:20:33 +12:00
parent 37419d9781
commit 2213c0dc32
9 changed files with 94 additions and 73 deletions

View file

@ -12,6 +12,8 @@ script:
printf 'Checking for tabs in %s\n' "$TRAVIS_COMMIT_RANGE" printf 'Checking for tabs in %s\n' "$TRAVIS_COMMIT_RANGE"
! git diff --name-only --diff-filter=ACMR "$TRAVIS_COMMIT_RANGE" | xargs grep $'\t' ! git diff --name-only --diff-filter=ACMR "$TRAVIS_COMMIT_RANGE" | xargs grep $'\t'
fi fi
- ( cd maud_htmlescape && cargo test --all-features )
- if command -v cargo-clippy > /dev/null; then ( cd maud_htmlescape && cargo clippy -- -D warnings ); fi
- ( cd maud && cargo test --all-features ) - ( cd maud && cargo test --all-features )
- if command -v cargo-clippy > /dev/null; then ( cd maud && cargo clippy -- -D warnings ); fi - if command -v cargo-clippy > /dev/null; then ( cd maud && cargo clippy -- -D warnings ); fi
- ( cd maud_macros && cargo test --all-features ) - ( cd maud_macros && cargo test --all-features )

View file

@ -1,6 +1,7 @@
[workspace] [workspace]
members = [ members = [
"maud", "maud_htmlescape",
"maud_macros", "maud_macros",
"maud",
"maud_extras", "maud_extras",
] ]

View file

@ -13,6 +13,7 @@ description = "Compile-time HTML templates."
categories = ["template-engine"] categories = ["template-engine"]
[dependencies] [dependencies]
maud_htmlescape = { version = "0.17.0", path = "../maud_htmlescape" }
maud_macros = { version = "0.16.3", path = "../maud_macros" } maud_macros = { version = "0.16.3", path = "../maud_macros" }
iron = { version = "0.5.1", optional = true } iron = { version = "0.5.1", optional = true }
rocket = { version = "0.3", optional = true } rocket = { version = "0.3", optional = true }

View file

@ -13,6 +13,7 @@
#[cfg(feature = "iron")] extern crate iron; #[cfg(feature = "iron")] extern crate iron;
#[cfg(feature = "rocket")] extern crate rocket; #[cfg(feature = "rocket")] extern crate rocket;
extern crate maud_htmlescape;
extern crate maud_macros; extern crate maud_macros;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
@ -121,52 +122,7 @@ impl<T: AsRef<str> + Into<String>> Into<String> for PreEscaped<T> {
} }
} }
/// An adapter that escapes HTML special characters. pub use maud_htmlescape::Escaper;
///
/// The following characters are escaped:
///
/// * `&` is escaped as `&amp;`
/// * `<` is escaped as `&lt;`
/// * `>` is escaped as `&gt;`
/// * `"` is escaped as `&quot;`
///
/// All other characters are passed through unchanged.
///
/// **Note:** In versions prior to 0.13, the single quote (`'`) was
/// escaped as well.
///
/// # Example
///
/// ```
/// # use maud::Escaper;
/// use std::fmt::Write;
/// let mut s = String::new();
/// write!(Escaper::new(&mut s), "<script>launchMissiles()</script>").unwrap();
/// assert_eq!(s, "&lt;script&gt;launchMissiles()&lt;/script&gt;");
/// ```
pub struct Escaper<'a>(&'a mut String);
impl<'a> Escaper<'a> {
/// Creates an `Escaper` from a `String`.
pub fn new(buffer: &'a mut String) -> Escaper<'a> {
Escaper(buffer)
}
}
impl<'a> fmt::Write for Escaper<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for b in s.bytes() {
match b {
b'&' => self.0.push_str("&amp;"),
b'<' => self.0.push_str("&lt;"),
b'>' => self.0.push_str("&gt;"),
b'"' => self.0.push_str("&quot;"),
_ => unsafe { self.0.as_mut_vec().push(b) },
}
}
Ok(())
}
}
/// The literal string `<!DOCTYPE html>`. /// The literal string `<!DOCTYPE html>`.
/// ///

View file

@ -0,0 +1,15 @@
[package]
name = "maud_htmlescape"
# When releasing a new version, please update html_root_url in lib.rs
version = "0.17.0"
authors = ["Chris Wong <lambda.fairy@gmail.com>"]
license = "MIT/Apache-2.0"
documentation = "https://docs.rs/maud_htmlescape/"
homepage = "https://maud.lambda.xyz/"
repository = "https://github.com/lfairy/maud"
description = "Internal support code used by Maud."
[lib]
path = "lib.rs"

68
maud_htmlescape/lib.rs Normal file
View file

@ -0,0 +1,68 @@
//! Internal support code used by the [Maud] template engine.
//!
//! You should not need to depend on this crate directly.
//!
//! [Maud]: https://maud.lambda.xyz
#![doc(html_root_url = "https://docs.rs/maud_htmlescape/0.17.0")]
use std::fmt;
/// An adapter that escapes HTML special characters.
///
/// The following characters are escaped:
///
/// * `&` is escaped as `&amp;`
/// * `<` is escaped as `&lt;`
/// * `>` is escaped as `&gt;`
/// * `"` is escaped as `&quot;`
///
/// All other characters are passed through unchanged.
///
/// **Note:** In versions prior to 0.13, the single quote (`'`) was
/// escaped as well.
///
/// # Example
///
/// ```rust,ignore
/// use std::fmt::Write;
/// let mut s = String::new();
/// write!(Escaper::new(&mut s), "<script>launchMissiles()</script>").unwrap();
/// assert_eq!(s, "&lt;script&gt;launchMissiles()&lt;/script&gt;");
/// ```
pub struct Escaper<'a>(&'a mut String);
impl<'a> Escaper<'a> {
/// Creates an `Escaper` from a `String`.
pub fn new(buffer: &'a mut String) -> Escaper<'a> {
Escaper(buffer)
}
}
impl<'a> fmt::Write for Escaper<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for b in s.bytes() {
match b {
b'&' => self.0.push_str("&amp;"),
b'<' => self.0.push_str("&lt;"),
b'>' => self.0.push_str("&gt;"),
b'"' => self.0.push_str("&quot;"),
_ => unsafe { self.0.as_mut_vec().push(b) },
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use std::fmt::Write;
use Escaper;
#[test]
fn it_works() {
let mut s = String::new();
write!(Escaper::new(&mut s), "<script>launchMissiles()</script>").unwrap();
assert_eq!(s, "&lt;script&gt;launchMissiles()&lt;/script&gt;");
}
}

View file

@ -13,6 +13,7 @@ description = "Compile-time HTML templates."
[dependencies] [dependencies]
literalext = { version = "0.1", default-features = false, features = ["proc-macro"] } literalext = { version = "0.1", default-features = false, features = ["proc-macro"] }
maud_htmlescape = { version = "0.17.0", path = "../maud_htmlescape" }
[lib] [lib]
name = "maud_macros" name = "maud_macros"

View file

@ -4,6 +4,7 @@
#![doc(html_root_url = "https://docs.rs/maud_macros/0.16.3")] #![doc(html_root_url = "https://docs.rs/maud_macros/0.16.3")]
extern crate literalext; extern crate literalext;
extern crate maud_htmlescape;
extern crate proc_macro; extern crate proc_macro;
// TODO move lints into their own `maud_lints` crate // TODO move lints into their own `maud_lints` crate

View file

@ -1,6 +1,7 @@
use proc_macro::{Literal, Term, TokenNode, TokenStream}; use proc_macro::{Literal, Term, TokenNode, TokenStream};
use proc_macro::quote; use proc_macro::quote;
use std::fmt;
use maud_htmlescape::Escaper;
pub struct Renderer { pub struct Renderer {
output: TokenNode, output: TokenNode,
@ -166,28 +167,3 @@ fn html_escape(s: &str) -> String {
Escaper::new(&mut buffer).write_str(s).unwrap(); Escaper::new(&mut buffer).write_str(s).unwrap();
buffer buffer
} }
// TODO move this into a common `maud_htmlescape` crate
struct Escaper<'a>(&'a mut String);
impl<'a> Escaper<'a> {
/// Creates an `Escaper` from a `String`.
pub fn new(buffer: &'a mut String) -> Escaper<'a> {
Escaper(buffer)
}
}
impl<'a> fmt::Write for Escaper<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for b in s.bytes() {
match b {
b'&' => self.0.push_str("&amp;"),
b'<' => self.0.push_str("&lt;"),
b'>' => self.0.push_str("&gt;"),
b'"' => self.0.push_str("&quot;"),
_ => unsafe { self.0.as_mut_vec().push(b) },
}
}
Ok(())
}
}