From 84da2bc853a26ca23065830c3e355dd0e8b3500c Mon Sep 17 00:00:00 2001 From: Roope Salmi Date: Mon, 22 Apr 2024 21:03:58 +0300 Subject: [PATCH] Use Display structs for escaping in html.rs --- pulldown-cmark/src/html.rs | 191 ++++++++++++++++++++++++++----------- 1 file changed, 135 insertions(+), 56 deletions(-) diff --git a/pulldown-cmark/src/html.rs b/pulldown-cmark/src/html.rs index 9819674a5..312d7cf6f 100644 --- a/pulldown-cmark/src/html.rs +++ b/pulldown-cmark/src/html.rs @@ -21,13 +21,93 @@ //! HTML renderer that takes an iterator of events as input. use std::collections::HashMap; +use std::fmt; +use std::io; use crate::strings::CowStr; use crate::Event::*; use crate::{Alignment, BlockQuoteKind, CodeBlockKind, Event, LinkType, Tag, TagEnd}; -use pulldown_cmark_escape::{ - escape_href, escape_html, escape_html_body_text, FmtWriter, IoWriter, StrWrite, -}; +use pulldown_cmark_escape::{EscapedHref, EscapedHtml, EscapedHtmlBodyText}; + +/// Trait that enables [`HtmlWriter`] to be generic over fmt/io writers. +trait StrWrite { + type Error; + + fn write_str(&mut self, s: &str) -> Result<(), Self::Error>; + fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), Self::Error>; +} + +#[derive(Debug)] +struct IoWriter(pub W); + +impl StrWrite for IoWriter +where + W: io::Write, +{ + type Error = io::Error; + + #[inline] + fn write_str(&mut self, s: &str) -> io::Result<()> { + self.0.write_all(s.as_bytes()) + } + + #[inline] + fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> { + self.0.write_fmt(args) + } +} + +#[derive(Debug)] +struct FmtWriter(pub W); + +impl StrWrite for FmtWriter +where + W: fmt::Write, +{ + type Error = fmt::Error; + + #[inline] + fn write_str(&mut self, s: &str) -> fmt::Result { + self.0.write_str(s) + } + + #[inline] + fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { + self.0.write_fmt(args) + } +} + +// impl StrWrite for String { +// type Error = fmt::Error; +// +// #[inline] +// fn write_str(&mut self, s: &str) -> fmt::Result { +// self.push_str(s); +// Ok(()) +// } +// +// #[inline] +// fn write_fmt(&mut self, args: Arguments) -> fmt::Result { +// fmt::Write::write_fmt(self, args) +// } +// } +// +// impl StrWrite for &'_ mut W +// where +// W: StrWrite, +// { +// type Error = W::Error; +// +// #[inline] +// fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { +// (**self).write_str(s) +// } +// +// #[inline] +// fn write_fmt(&mut self, args: Arguments) -> Result<(), Self::Error> { +// (**self).write_fmt(args) +// } +// } enum TableState { Head, @@ -100,24 +180,26 @@ where } Text(text) => { if !self.in_non_writing_block { - escape_html_body_text(&mut self.writer, &text)?; + write!(self.writer, "{}", EscapedHtmlBodyText(&text))?; self.end_newline = text.ends_with('\n'); } } Code(text) => { - self.write("")?; - escape_html_body_text(&mut self.writer, &text)?; - self.write("")?; + write!(self.writer, "{}", EscapedHtmlBodyText(&text))?; } InlineMath(text) => { - self.write(r#""#)?; - escape_html(&mut self.writer, &text)?; - self.write("")?; + write!( + self.writer, + r#"{}"#, + EscapedHtmlBodyText(&text) + )?; } DisplayMath(text) => { - self.write(r#""#)?; - escape_html(&mut self.writer, &text)?; - self.write("")?; + write!( + self.writer, + r#"{}"#, + EscapedHtmlBodyText(&text) + )?; } Html(html) | InlineHtml(html) => { self.write(&html)?; @@ -137,11 +219,13 @@ where } FootnoteReference(name) => { let len = self.numbers.len() + 1; - self.write("")?; + write!( + self.writer, + "", + EscapedHtml(&name) + )?; let number = *self.numbers.entry(name).or_insert(len); - write!(&mut self.writer, "{}", number)?; + write!(self.writer, "{}", number)?; self.write("")?; } TaskListMarker(true) => { @@ -178,29 +262,22 @@ where } else { self.write("\n<")?; } - write!(&mut self.writer, "{}", level)?; + write!(self.writer, "{}", level)?; if let Some(id) = id { - self.write(" id=\"")?; - escape_html(&mut self.writer, &id)?; - self.write("\"")?; + write!(self.writer, " id=\"{}\"", EscapedHtml(&id))?; } let mut classes = classes.iter(); if let Some(class) = classes.next() { - self.write(" class=\"")?; - escape_html(&mut self.writer, class)?; + write!(self.writer, " class=\"{}", EscapedHtml(class))?; for class in classes { - self.write(" ")?; - escape_html(&mut self.writer, class)?; + write!(self.writer, " {}", EscapedHtml(class))?; } self.write("\"")?; } for (attr, value) in attrs { - self.write(" ")?; - escape_html(&mut self.writer, &attr)?; + write!(self.writer, " {}", EscapedHtml(&attr))?; if let Some(val) = value { - self.write("=\"")?; - escape_html(&mut self.writer, &val)?; - self.write("\"")?; + write!(self.writer, "=\"{}\"", EscapedHtml(&val))?; } else { self.write("=\"\"")?; } @@ -263,9 +340,11 @@ where if lang.is_empty() { self.write("
")
                         } else {
-                            self.write("
")
+                            write!(
+                                self.writer,
+                                "
",
+                                EscapedHtml(lang)
+                            )
                         }
                     }
                     CodeBlockKind::Indented => self.write("
"),
@@ -284,7 +363,7 @@ where
                 } else {
                     self.write("\n
    \n") } Tag::List(None) => { @@ -310,11 +389,9 @@ where title, id: _, } => { - self.write("") } @@ -324,11 +401,9 @@ where title, id: _, } => { - self.write("") } @@ -338,13 +413,14 @@ where title, id: _, } => { - self.write("\"")?;") } @@ -354,11 +430,14 @@ where } else { self.write("\n
    ")?; + write!( + self.writer, + "{}\">", + EscapedHtml(&name) + )?; let len = self.numbers.len() + 1; let number = *self.numbers.entry(name).or_insert(len); - write!(&mut self.writer, "{}", number)?; + write!(self.writer, "{}", number)?; self.write("") } Tag::MetadataBlock(_) => { @@ -376,7 +455,7 @@ where } TagEnd::Heading(level) => { self.write("\n")?; } TagEnd::Table => { @@ -452,19 +531,19 @@ where } Html(_) => {} InlineHtml(text) | Code(text) | Text(text) => { - // Don't use escape_html_body_text here. + // Don't use EscapedHtmlBodyText here. // The output of this function is used in the `alt` attribute. - escape_html(&mut self.writer, &text)?; + write!(self.writer, "{}", EscapedHtml(&text))?; self.end_newline = text.ends_with('\n'); } InlineMath(text) => { self.write("$")?; - escape_html(&mut self.writer, &text)?; + write!(self.writer, "{}", EscapedHtml(&text))?; self.write("$")?; } DisplayMath(text) => { self.write("$$")?; - escape_html(&mut self.writer, &text)?; + write!(self.writer, "{}", EscapedHtml(&text))?; self.write("$$")?; } SoftBreak | HardBreak | Rule => { @@ -473,7 +552,7 @@ where FootnoteReference(name) => { let len = self.numbers.len() + 1; let number = *self.numbers.entry(name).or_insert(len); - write!(&mut self.writer, "[{}]", number)?; + write!(self.writer, "[{}]", number)?; } TaskListMarker(true) => self.write("[x]")?, TaskListMarker(false) => self.write("[ ]")?,