101 lines
3.4 KiB
Rust
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>"##
|
||
|
);
|
||
|
}
|
||
|
}
|