diff options
Diffstat (limited to '')
-rw-r--r-- | askama_shared/src/escaping.rs | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/askama_shared/src/escaping.rs b/askama_shared/src/escaping.rs new file mode 100644 index 0000000..54f58e4 --- /dev/null +++ b/askama_shared/src/escaping.rs @@ -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"); + } +} |