zola/components/rendering/src/table_of_contents.rs

112 lines
2.8 KiB
Rust
Raw Normal View History

2019-01-27 23:34:18 +00:00
/// Populated while receiving events from the markdown parser
2017-06-16 04:00:48 +00:00
#[derive(Debug, PartialEq, Clone, Serialize)]
pub struct Header {
#[serde(skip_serializing)]
pub level: i32,
pub id: String,
pub permalink: String,
2019-01-27 23:34:18 +00:00
pub title: String,
2017-06-16 04:00:48 +00:00
pub children: Vec<Header>,
}
impl Header {
2019-01-27 23:34:18 +00:00
pub fn new(level: i32) -> Header {
2017-06-16 04:00:48 +00:00
Header {
level,
id: String::new(),
permalink: String::new(),
title: String::new(),
2019-01-27 23:34:18 +00:00
children: Vec::new(),
2017-09-27 14:09:13 +00:00
}
}
2017-06-16 04:00:48 +00:00
}
2019-01-27 23:34:18 +00:00
impl Default for Header {
2017-06-16 04:00:48 +00:00
fn default() -> Self {
2019-01-27 23:34:18 +00:00
Header::new(0)
2017-06-16 04:00:48 +00:00
}
}
/// Converts the flat temp headers into a nested set of headers
/// representing the hierarchy
2019-01-27 23:34:18 +00:00
pub fn make_table_of_contents(headers: Vec<Header>) -> Vec<Header> {
2017-06-16 04:00:48 +00:00
let mut toc = vec![];
2019-01-27 23:34:18 +00:00
'parent: for header in headers {
if toc.is_empty() {
toc.push(header);
2017-06-16 04:00:48 +00:00
continue;
}
2019-01-27 23:34:18 +00:00
// See if we have to insert as a child of a previous header
for h in toc.iter_mut().rev() {
// Look in its children first
for child in h.children.iter_mut().rev() {
if header.level > child.level {
child.children.push(header);
continue 'parent;
}
}
if header.level > h.level {
h.children.push(header);
continue 'parent;
}
}
// Nop, just insert it
toc.push(header)
2017-06-16 04:00:48 +00:00
}
toc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_make_basic_toc() {
2019-01-27 23:34:18 +00:00
let input = vec![Header::new(1), Header::new(1), Header::new(1)];
let toc = make_table_of_contents(input);
2017-06-16 04:00:48 +00:00
assert_eq!(toc.len(), 3);
}
#[test]
fn can_make_more_complex_toc() {
let input = vec![
2019-01-27 23:34:18 +00:00
Header::new(1),
Header::new(2),
Header::new(2),
Header::new(3),
Header::new(2),
Header::new(1),
Header::new(2),
Header::new(3),
Header::new(3),
2017-06-16 04:00:48 +00:00
];
2019-01-27 23:34:18 +00:00
let toc = make_table_of_contents(input);
2017-06-16 04:00:48 +00:00
assert_eq!(toc.len(), 2);
assert_eq!(toc[0].children.len(), 3);
assert_eq!(toc[1].children.len(), 1);
assert_eq!(toc[0].children[1].children.len(), 1);
assert_eq!(toc[1].children[0].children.len(), 2);
}
#[test]
fn can_make_messy_toc() {
let input = vec![
2019-01-27 23:34:18 +00:00
Header::new(3),
Header::new(2),
Header::new(2),
Header::new(3),
Header::new(2),
Header::new(1),
Header::new(4),
2017-06-16 04:00:48 +00:00
];
2019-01-27 23:34:18 +00:00
let toc = make_table_of_contents(input);
println!("{:#?}", toc);
2017-06-16 04:00:48 +00:00
assert_eq!(toc.len(), 5);
assert_eq!(toc[2].children.len(), 1);
assert_eq!(toc[4].children.len(), 1);
}
}