mirror of
https://github.com/askama-rs/askama.git
synced 2025-10-02 15:25:19 +00:00
Move escaping algorithm into a separate module
This commit is contained in:
parent
23279c3bce
commit
fdbe45ec60
50
askama_shared/src/escaping.rs
Normal file
50
askama_shared/src/escaping.rs
Normal file
@ -0,0 +1,50 @@
|
||||
fn escapable(b: &u8) -> bool {
|
||||
*b == b'<' || *b == b'>' || *b == b'&'
|
||||
}
|
||||
|
||||
pub fn escape(s: String) -> String {
|
||||
let mut found = Vec::new();
|
||||
for (i, b) in s.as_bytes().iter().enumerate() {
|
||||
if escapable(b) {
|
||||
found.push(i);
|
||||
}
|
||||
}
|
||||
if found.is_empty() {
|
||||
return s;
|
||||
}
|
||||
|
||||
let bytes = s.as_bytes();
|
||||
let max_len = bytes.len() + found.len() * 3;
|
||||
let mut res = Vec::<u8>::with_capacity(max_len);
|
||||
let mut start = 0;
|
||||
for idx in &found {
|
||||
if start < *idx {
|
||||
res.extend(&bytes[start..*idx]);
|
||||
}
|
||||
start = *idx + 1;
|
||||
match bytes[*idx] {
|
||||
b'<' => { res.extend(b"<"); },
|
||||
b'>' => { res.extend(b">"); },
|
||||
b'&' => { res.extend(b"&"); },
|
||||
_ => panic!("incorrect indexing"),
|
||||
}
|
||||
}
|
||||
if start < bytes.len() - 1 {
|
||||
res.extend(&bytes[start..]);
|
||||
}
|
||||
|
||||
String::from_utf8(res).unwrap()
|
||||
}
|
||||
|
||||
|
||||
#[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("<foo".to_string()), "<foo");
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ pub use self::json::json;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use escaping;
|
||||
use super::Result;
|
||||
|
||||
|
||||
@ -33,44 +34,10 @@ pub const BUILT_IN_FILTERS: [&str; 9] = [
|
||||
];
|
||||
|
||||
|
||||
fn escapable(b: &u8) -> bool {
|
||||
*b == b'<' || *b == b'>' || *b == b'&'
|
||||
}
|
||||
|
||||
/// Escapes `&`, `<` and `>` in strings
|
||||
pub fn escape(s: &fmt::Display) -> Result<String> {
|
||||
let s = format!("{}", s);
|
||||
let mut found = Vec::new();
|
||||
for (i, b) in s.as_bytes().iter().enumerate() {
|
||||
if escapable(b) {
|
||||
found.push(i);
|
||||
}
|
||||
}
|
||||
if found.is_empty() {
|
||||
return Ok(s);
|
||||
}
|
||||
|
||||
let bytes = s.as_bytes();
|
||||
let max_len = bytes.len() + found.len() * 3;
|
||||
let mut res = Vec::<u8>::with_capacity(max_len);
|
||||
let mut start = 0;
|
||||
for idx in &found {
|
||||
if start < *idx {
|
||||
res.extend(&bytes[start..*idx]);
|
||||
}
|
||||
start = *idx + 1;
|
||||
match bytes[*idx] {
|
||||
b'<' => { res.extend(b"<"); },
|
||||
b'>' => { res.extend(b">"); },
|
||||
b'&' => { res.extend(b"&"); },
|
||||
_ => panic!("incorrect indexing"),
|
||||
}
|
||||
}
|
||||
if start < bytes.len() - 1 {
|
||||
res.extend(&bytes[start..]);
|
||||
}
|
||||
|
||||
Ok(String::from_utf8(res).unwrap())
|
||||
Ok(escaping::escape(s))
|
||||
}
|
||||
|
||||
/// Alias for the `escape()` filter
|
||||
@ -135,17 +102,10 @@ pub fn join<T, I, S>(input: I, separator: S) -> Result<String>
|
||||
Ok(rv)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_escape() {
|
||||
assert_eq!(escape(&"").unwrap(), "");
|
||||
assert_eq!(escape(&"<&>").unwrap(), "<&>");
|
||||
assert_eq!(escape(&"bla&").unwrap(), "bla&");
|
||||
assert_eq!(escape(&"<foo").unwrap(), "<foo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower() {
|
||||
assert_eq!(lower(&"Foo").unwrap(), "foo");
|
||||
|
@ -16,6 +16,7 @@ pub mod path;
|
||||
pub use parser::parse;
|
||||
pub use generator::generate;
|
||||
|
||||
mod escaping;
|
||||
mod generator;
|
||||
mod parser;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user