1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
use std::fmt; fn escapable(b: &u8) -> bool { *b == b'<' || *b == b'>' || *b == b'&' } pub fn escape(s: &fmt::Display) -> 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 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() } pub fn e(s: &fmt::Display) -> String { escape(s) } #[cfg(test)] mod tests { use super::*; #[test] fn test_escape() { assert_eq!(escape(&""), ""); assert_eq!(escape(&"<&>"), "<&>"); assert_eq!(escape(&"bla&"), "bla&"); assert_eq!(escape(&"<foo"), "<foo"); } }