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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
use nom::*; use std::fmt::{self, Display, Write}; struct EscapingWriter<'a> { inner: &'a mut Write } impl<'a> EscapingWriter<'a> { fn new(inner: &'a mut Write) -> EscapingWriter<'a> { EscapingWriter { inner: inner } } } named!(part(&str) -> &str, alt!( map!(tag!("<"), |_| "<" ) | map!(tag!("&"), |_| "&" ) | map!(tag!("\""), |_| """ ) | map!(tag!("'"), |_| "'" ) | is_not!("<&\"'") ) ); impl<'a> Write for EscapingWriter<'a> { fn write_str(&mut self, buf: &str) -> fmt::Result { let mut rest = buf; while let IResult::Done(new_rest, parsed) = part(&rest) { self.inner.write_str(parsed)?; rest = new_rest; } Ok(()) } } pub trait DisplayHtmlSafe { fn safe_fmt(&self, &mut fmt::Formatter) -> fmt::Result; } impl<T: Display> DisplayHtmlSafe for T { #[cfg(feature = "specialization")] default fn safe_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut escaping_writer = EscapingWriter::new(f); write!(&mut escaping_writer, "{}", &self) } #[cfg(not(feature = "specialization"))] fn safe_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut escaping_writer = EscapingWriter::new(f); write!(&mut escaping_writer, "{}", &self) } } macro_rules! display_is_html_safe { ($x : ident) => { #[cfg(feature = "specialization")] impl DisplayHtmlSafe for $x { fn safe_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Display::fmt(&self, f) } } } } display_is_html_safe!(u8); display_is_html_safe!(i8); display_is_html_safe!(u16); display_is_html_safe!(i16); display_is_html_safe!(u32); display_is_html_safe!(i32); display_is_html_safe!(u64); display_is_html_safe!(i64); display_is_html_safe!(usize); display_is_html_safe!(isize); display_is_html_safe!(f32); display_is_html_safe!(f64); display_is_html_safe!(bool); #[cfg(test)] mod test { use super::*; struct Fake<'a> { text: &'a str } impl<'a> Display for Fake<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.text.safe_fmt(f) } } #[test] fn it_works() { assert_eq!( " < & text " ' ", format!("{}", Fake { text: " < & text \" ' " }) ); } #[test] fn it_handles_tight_packed_string() { assert_eq!( "<te&"xt'", format!("{}", Fake { text: "<te&\"xt'" }) ); } }