From 2cb74028b6190cc92dfe2ef1188fa0e9b0733f6e Mon Sep 17 00:00:00 2001 From: PoiScript Date: Fri, 15 Feb 2019 00:05:50 +0800 Subject: [PATCH] feat(export): wrapper struct Escape --- src/export/html.rs | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/export/html.rs b/src/export/html.rs index 961f96c..3348885 100644 --- a/src/export/html.rs +++ b/src/export/html.rs @@ -1,9 +1,13 @@ #![allow(unused_variables)] +use std::fmt; +use std::io::{Result, Write}; + +use jetscii::ascii_chars; + use crate::elements::Key; use crate::headline::Headline; use crate::objects::Cookie; -use std::io::{Result, Write}; pub trait HtmlHandler { fn handle_headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<()> { @@ -188,6 +192,42 @@ pub trait HtmlHandler { write!(w, "{}", cont) } fn handle_text(&mut self, w: &mut W, cont: &str) -> Result<()> { - write!(w, "{}", cont.replace('\n', " ")) + write!(w, "{}", Escape(&cont)) + } +} + +pub struct Escape<'a>(&'a str); + +impl<'a> fmt::Display for Escape<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut pos = 0; + while let Some(off) = ascii_chars!('<', '>', '&', '\'', '"').find(&self.0[pos..]) { + fmt.write_str(&self.0[pos..pos + off])?; + + pos += off + 1; + + match &self.0.as_bytes()[pos - 1] { + b'"' => fmt.write_str(""")?, + b'&' => fmt.write_str("&")?, + b'<' => fmt.write_str("<")?, + b'>' => fmt.write_str(">")?, + b'\'' => fmt.write_str("'")?, + b'\n' => fmt.write_str(" ")?, + _ => unreachable!(), + } + } + + fmt.write_str(&self.0[pos..]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn escape() { + assert_eq!(format!("{}", Escape("<<<<<<")), "<<<<<<"); + assert_eq!(format!("{}", Escape(" <> <> ")), " <> <> "); } }