From 8224f06f3e900443ee42d75cba30d4b9f5f6fd95 Mon Sep 17 00:00:00 2001 From: bott Date: Wed, 7 Nov 2018 13:53:30 +0100 Subject: Create askama_escape crate --- Cargo.toml | 2 +- askama_escape/Cargo.toml | 16 +++++++ askama_escape/src/lib.rs | 100 +++++++++++++++++++++++++++++++++++++++ askama_shared/Cargo.toml | 1 + askama_shared/src/escaping.rs | 100 --------------------------------------- askama_shared/src/filters/mod.rs | 2 +- askama_shared/src/lib.rs | 4 +- 7 files changed, 121 insertions(+), 104 deletions(-) create mode 100644 askama_escape/Cargo.toml create mode 100644 askama_escape/src/lib.rs delete mode 100644 askama_shared/src/escaping.rs diff --git a/Cargo.toml b/Cargo.toml index cce1ee3..286fd1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["askama", "askama_derive", "askama_shared", "testing"] +members = ["askama", "askama_derive", "askama_escape", "askama_shared", "testing"] diff --git a/askama_escape/Cargo.toml b/askama_escape/Cargo.toml new file mode 100644 index 0000000..c6af27d --- /dev/null +++ b/askama_escape/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "askama_escape" +version = "0.1.0" +authors = ["Dirkjan Ochtman "] +description = "Escape function for Askama" +homepage = "https://github.com/djc/askama" +repository = "https://github.com/djc/askama" +license = "MIT/Apache-2.0" +workspace = ".." + +[features] +default = [] +serde-json = [] +iron = [] +rocket = [] +actix-web = [] diff --git a/askama_escape/src/lib.rs b/askama_escape/src/lib.rs new file mode 100644 index 0000000..b967f1f --- /dev/null +++ b/askama_escape/src/lib.rs @@ -0,0 +1,100 @@ +use std::fmt::{self, Display, Formatter}; +use std::str; + +#[derive(Debug, PartialEq)] +pub enum MarkupDisplay +where + T: Display, +{ + Safe(T), + Unsafe(T), +} + +impl MarkupDisplay +where + T: Display, +{ + pub fn mark_safe(self) -> MarkupDisplay { + match self { + MarkupDisplay::Unsafe(t) => MarkupDisplay::Safe(t), + _ => self, + } + } +} + +impl From for MarkupDisplay +where + T: Display, +{ + fn from(t: T) -> MarkupDisplay { + MarkupDisplay::Unsafe(t) + } +} + +impl Display for MarkupDisplay +where + T: Display, +{ + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match *self { + MarkupDisplay::Unsafe(ref t) => escape(&t.to_string()).fmt(f), + MarkupDisplay::Safe(ref t) => t.fmt(f), + } + } +} + +pub fn escape(s: &str) -> Escaped { + Escaped { + bytes: s.as_bytes(), + } +} + +macro_rules! escaping_body { + ($start:ident, $i:ident, $fmt:ident, $_self:ident, $quote:expr) => {{ + if $start < $i { + $fmt.write_str(unsafe { str::from_utf8_unchecked(&$_self.bytes[$start..$i]) })?; + } + $fmt.write_str($quote)?; + $start = $i + 1; + }}; +} + +pub struct Escaped<'a> { + bytes: &'a [u8], +} + +impl<'a> ::std::fmt::Display for Escaped<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut start = 0; + for (i, b) in self.bytes.iter().enumerate() { + if b.wrapping_sub(b'"') <= FLAG { + match *b { + b'<' => escaping_body!(start, i, fmt, self, "<"), + b'>' => escaping_body!(start, i, fmt, self, ">"), + b'&' => escaping_body!(start, i, fmt, self, "&"), + b'"' => escaping_body!(start, i, fmt, self, """), + b'\'' => escaping_body!(start, i, fmt, self, "'"), + b'/' => escaping_body!(start, i, fmt, self, "/"), + _ => (), + } + } + } + fmt.write_str(unsafe { str::from_utf8_unchecked(&self.bytes[start..]) })?; + Ok(()) + } +} + +const FLAG: u8 = b'>' - b'"'; + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_escape() { + assert_eq!(escape("").to_string(), ""); + assert_eq!(escape("<&>").to_string(), "<&>"); + assert_eq!(escape("bla&").to_string(), "bla&"); + assert_eq!(escape(" -where - T: Display, -{ - Safe(T), - Unsafe(T), -} - -impl MarkupDisplay -where - T: Display, -{ - pub fn mark_safe(self) -> MarkupDisplay { - match self { - MarkupDisplay::Unsafe(t) => MarkupDisplay::Safe(t), - _ => self, - } - } -} - -impl From for MarkupDisplay -where - T: Display, -{ - fn from(t: T) -> MarkupDisplay { - MarkupDisplay::Unsafe(t) - } -} - -impl Display for MarkupDisplay -where - T: Display, -{ - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match *self { - MarkupDisplay::Unsafe(ref t) => escape(&t.to_string()).fmt(f), - MarkupDisplay::Safe(ref t) => t.fmt(f), - } - } -} - -pub fn escape(s: &str) -> Escaped { - Escaped { - bytes: s.as_bytes(), - } -} - -macro_rules! escaping_body { - ($start:ident, $i:ident, $fmt:ident, $_self:ident, $quote:expr) => {{ - if $start < $i { - $fmt.write_str(unsafe { str::from_utf8_unchecked(&$_self.bytes[$start..$i]) })?; - } - $fmt.write_str($quote)?; - $start = $i + 1; - }}; -} - -pub struct Escaped<'a> { - bytes: &'a [u8], -} - -impl<'a> ::std::fmt::Display for Escaped<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let mut start = 0; - for (i, b) in self.bytes.iter().enumerate() { - if b.wrapping_sub(b'"') <= FLAG { - match *b { - b'<' => escaping_body!(start, i, fmt, self, "<"), - b'>' => escaping_body!(start, i, fmt, self, ">"), - b'&' => escaping_body!(start, i, fmt, self, "&"), - b'"' => escaping_body!(start, i, fmt, self, """), - b'\'' => escaping_body!(start, i, fmt, self, "'"), - b'/' => escaping_body!(start, i, fmt, self, "/"), - _ => (), - } - } - } - fmt.write_str(unsafe { str::from_utf8_unchecked(&self.bytes[start..]) })?; - Ok(()) - } -} - -const FLAG: u8 = b'>' - b'"'; - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn test_escape() { - assert_eq!(escape("").to_string(), ""); - assert_eq!(escape("<&>").to_string(), "<&>"); - assert_eq!(escape("bla&").to_string(), "bla&"); - assert_eq!(escape("