v6/src/generator/markdown/footnote_defs.rs

101 lines
3.4 KiB
Rust

use pulldown_cmark::{CowStr, Event, Tag};
use std::collections::VecDeque;
pub fn new<'a, I: Iterator<Item = Event<'a>>>(iter: I) -> FootnoteDefs<'a, I> {
FootnoteDefs {
iter,
footnote_events: VecDeque::new(),
has_started_emitting_defs: false,
}
}
pub struct FootnoteDefs<'a, I: Iterator<Item = Event<'a>>> {
iter: I,
footnote_events: VecDeque<Event<'a>>,
has_started_emitting_defs: bool,
}
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for FootnoteDefs<'a, I> {
type Item = Event<'a>;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
Some(Event::Start(Tag::FootnoteDefinition(ref id))) => {
self.footnote_events.push_back(Event::Html(
format!(
r#"<div id="{}" class="footnote-item"><span class="footnote-marker">{}.</span>"#,
id, id
)
.into(),
));
loop {
match self.iter.next() {
Some(Event::End(Tag::FootnoteDefinition(_))) => {
self.footnote_events.push_back(Event::Html("</div>".into()));
break;
}
Some(e) => {
self.footnote_events.push_back(e);
}
None => {
break;
}
}
}
self.next()
}
Some(e) => Some(e),
None => {
if !self.has_started_emitting_defs && !self.footnote_events.is_empty() {
self.has_started_emitting_defs = true;
let before: CowStr<'a> =
r#"<hr class="footnotes-sep"><section class="footnotes">"#.into();
self.footnote_events.push_front(Event::Html(before));
let after: CowStr<'a> = "</section>".into();
self.footnote_events.push_back(Event::Html(after));
}
self.footnote_events.pop_front()
}
}
}
}
#[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_group_footnotes() {
assert_eq!(
render("foo[^1]\n\n[^1]: bar\n\nbaz"),
r##"<p>foo<sup class="footnote-reference"><a href="#1">1</a></sup></p>
<p>baz</p>
<hr class="footnotes-sep"><section class="footnotes"><div id="1" class="footnote-item"><span class="footnote-marker">1.</span>
<p>bar</p>
</div></section>"##
);
}
#[test]
fn test_multiple() {
assert_eq!(
render("foo[^1] bar[^2]\n\n[^1]: foo\n\n[^2]: bar"),
r##"<p>foo<sup class="footnote-reference"><a href="#1">1</a></sup> bar<sup class="footnote-reference"><a href="#2">2</a></sup></p>
<hr class="footnotes-sep"><section class="footnotes"><div id="1" class="footnote-item"><span class="footnote-marker">1.</span>
<p>foo</p>
</div><div id="2" class="footnote-item"><span class="footnote-marker">2.</span>
<p>bar</p>
</div></section>"##
);
}
}