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!("<"), |_| "&lt;" ) |
        map!(tag!("&"), |_| "&amp;" ) |
        map!(tag!("\""), |_| "&quot;" ) |
        map!(tag!("'"), |_| "&apos;" ) |
        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!(
            " &lt; &amp; text &quot; &apos; ",
            format!("{}", Fake { text: " < & text \" ' " })
        );
    }

    #[test]
    fn it_handles_tight_packed_string() {
        assert_eq!(
            "&lt;te&amp;&quot;xt&apos;",
            format!("{}", Fake { text: "<te&\"xt'" })
        );
    }
}