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"); +    } +} | 
