use std::cmp::Ordering;
use jotdown::{Container, Event, Render};
use crate::TocOpts;
#[derive(Default)]
pub struct TocRenderer {
opts: TocOpts,
current_level: u16,
nesting: u16,
in_heading: bool,
title_skipped: bool,
}
impl TocRenderer {
pub fn new(opts: TocOpts) -> Self {
TocRenderer {
opts,
current_level: 0,
nesting: 0,
in_heading: false,
title_skipped: false,
}
}
}
impl<'s> Render<'s> for TocRenderer {
fn push_event<W>(&mut self, event: Event<'_>, out: W) -> std::fmt::Result
where
W: std::fmt::Write,
{
self.render_event(&event, out)
}
}
impl TocRenderer {
fn render_event<W>(&mut self, e: &Event<'_>, mut out: W) -> std::fmt::Result
where
W: std::fmt::Write,
{
match e {
Event::Start(Container::Document, _) => self.render_prologue(out)?,
Event::End(Container::Document) => self.render_epilogue(out)?,
Event::Start(
Container::Heading {
level,
has_section: _,
id,
},
_attrs,
) => {
if !self.opts.skip_title || self.title_skipped {
self.in_heading = true;
match self.current_level.cmp(level) {
Ordering::Less => {
self.current_level = *level;
self.nesting += 1;
out.write_str(if self.nesting > 1 {
"\n<ul>\n"
} else {
"<ol>\n"
})?;
}
Ordering::Greater => {
while self.current_level > *level {
if self.nesting > 1 {
self.nesting -= 1;
out.write_str("</li>\n</ul>\n</li>\n")?;
}
self.current_level -= 1;
}
}
Ordering::Equal => {
out.write_str("</li>\n")?;
}
}
out.write_str("<li><a href=\"#")?;
out.write_str(id)?;
out.write_str("\">")?;
}
}
Event::End(Container::Heading {
level,
has_section: _,
id: _,
}) => {
if !self.opts.skip_title || self.in_heading {
out.write_str("</a>")?;
}
self.in_heading = false;
if *level == 1 && self.opts.skip_title {
self.title_skipped = true;
}
}
Event::Str(s) => {
if self.in_heading {
out.write_str(s)?;
}
}
_ => (),
}
Ok(())
}
fn render_prologue<W>(&mut self, mut out: W) -> std::fmt::Result
where
W: std::fmt::Write,
{
out.write_str(&self.opts.prologue)?;
out.write_str("\n")
}
fn render_epilogue<W>(&mut self, mut out: W) -> std::fmt::Result
where
W: std::fmt::Write,
{
while self.nesting > 1 {
out.write_str("\n</ul>\n")?;
self.nesting -= 1;
}
if self.nesting > 0 {
out.write_str("</li>\n</ol>\n")?;
}
out.write_str(&self.opts.epilogue)?;
Ok(())
}
}