use crate::{Event, Tag};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::ops::Range;
#[derive(Debug, Clone)]
struct Node<'e>(u32, Vec<Event<'e>>);
impl<'e> Node<'e> {
fn new(level: u32) -> Self {
Self(level, Vec::with_capacity(1))
}
fn push(&mut self, e: Event<'e>) {
self.1.push(e)
}
}
#[derive(Debug, Clone)]
struct List<'e>(RefCell<VecDeque<Node<'e>>>);
impl<'e> List<'e> {
fn new() -> Self {
List(RefCell::new(VecDeque::new()))
}
fn create(&self, level: u32) {
self.0.borrow_mut().push_back(Node::new(level));
}
fn add(&self, event: Event<'e>) {
let index = self.0.borrow().len() - 1;
self.0.borrow_mut().get_mut(index).unwrap().push(event);
}
}
#[derive(Debug, Clone)]
pub struct ToC<'e> {
range: Range<u32>,
list: List<'e>,
}
impl<'e> ToC<'e> {
pub fn new(range: Range<u32>) -> Self {
Self {
range,
list: List::new(),
}
}
pub fn start<'f>(&self, event: &Event<'f>) -> bool {
match &event {
Event::Start(Tag::Heading(n)) if self.range.contains(&n) => {
self.list.create(*n);
true
}
_ => false,
}
}
pub fn end<'f>(&self, event: &Event<'f>) -> bool {
match &event {
Event::End(Tag::Heading(_)) => true,
_ => false,
}
}
pub fn within(&self, e: Event<'e>) -> Option<Event<'e>> {
self.list.add(e.clone());
Some(e)
}
pub fn generate(&self) -> impl Iterator<Item = Event<'e>> {
let mut v = vec![];
v.push(Event::Html("<div class=\"sidebar toc\">\n".into()));
let mut indent = if self.range.start - 1 > 0 {
self.range.start - 1
} else {
0
};
for Node(level, ref mut events) in self.list.0.borrow_mut().iter_mut() {
push_tag(&mut v, indent, *level);
v.push(Event::Start(Tag::Item));
v.append(events);
v.push(Event::End(Tag::Item));
indent = *level;
}
while indent > 0 {
v.push(Event::End(Tag::List(None)));
indent -= 1;
}
v.push(Event::Html("</div>\n".into()));
v.into_iter()
}
}
fn push_tag<'e>(v: &mut Vec<Event<'e>>, indent: u32, level: u32) {
let mut sub = if level > indent {
level - indent
} else {
indent - level
};
while sub > 0 {
if level > indent {
v.push(Event::Start(Tag::List(None)));
sub -= 1;
} else {
v.push(Event::End(Tag::List(None)));
sub -= 1;
}
}
}