v6/src/generator/markdown/footnote_backrefs.rs

74 lines
2.3 KiB
Rust

use pulldown_cmark::{CowStr, Event, Tag};
use std::iter::Peekable;
pub struct FootnoteBackrefs<'a, I: Iterator<Item = Event<'a>>> {
iter: Peekable<I>,
next: Option<Event<'a>>,
}
pub fn new<'a, I: Iterator<Item = Event<'a>>>(iter: I) -> FootnoteBackrefs<'a, I> {
FootnoteBackrefs {
iter: iter.peekable(),
next: None,
}
}
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for FootnoteBackrefs<'a, I> {
type Item = Event<'a>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(e) = self.next.take() {
return Some(e);
}
match self.iter.next() {
Some(Event::FootnoteReference(label)) => {
let html = format!(
r##"<sup class="footnote-reference" id="fnref{}"><a href="#{}">[{}]</a></sup>"##,
label, label, label
);
Some(Event::Html(CowStr::Boxed(html.into_boxed_str())))
}
Some(Event::End(Tag::Paragraph)) => {
if let Some(Event::End(Tag::FootnoteDefinition(label))) = self.iter.peek() {
assert!(self.next.is_none());
self.next = Some(Event::End(Tag::Paragraph));
let html = format!(
r##" <a href="#fnref{}" class="footnote-backref">↩</a>"##,
label
);
Some(Event::Html(CowStr::Boxed(html.into_boxed_str())))
} else {
Some(Event::End(Tag::Paragraph))
}
}
e => e,
}
}
}
#[cfg(test)]
mod tests {
use pulldown_cmark::{html, Options, Parser};
fn render(s: &str) -> String {
let mut out = String::new();
let parser = Parser::new_ext(s, Options::ENABLE_FOOTNOTES);
let footnote_backrefs = super::new(parser);
html::push_html(&mut out, footnote_backrefs);
out
}
#[test]
fn test_footnote_backrefs() {
assert_eq!(
render("foo[^1]\n\n[^1]: bar"),
r##"<p>foo<sup class="footnote-reference" id="fnref1"><a href="#1">[1]</a></sup></p>
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
<p>bar <a href="#fnref1" class="footnote-backref">↩</a></p>
</div>
"##
);
}
}