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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-15 08:19:32 +00:00
|
|
|
// Takes a potential (mutable) parent and a header to try and insert into
|
|
|
|
// Returns true when it performed the insertion, false otherwise
|
|
|
|
fn insert_into_parent(potential_parent: Option<&mut Header>, header: &Header) -> bool {
|
|
|
|
match potential_parent {
|
|
|
|
None => {
|
|
|
|
// No potential parent to insert into so it needs to be insert higher
|
2019-08-24 20:23:08 +00:00
|
|
|
false
|
|
|
|
}
|
2019-08-15 08:19:32 +00:00
|
|
|
Some(parent) => {
|
|
|
|
let diff = header.level - parent.level;
|
|
|
|
if diff <= 0 {
|
|
|
|
// Heading is same level or higher so we don't insert here
|
|
|
|
return false;
|
2019-08-24 20:23:08 +00:00
|
|
|
}
|
2019-08-15 08:19:32 +00:00
|
|
|
if diff == 1 {
|
|
|
|
// We have a direct child of the parent
|
|
|
|
parent.children.push(header.clone());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// We need to go deeper
|
|
|
|
if !insert_into_parent(parent.children.iter_mut().last(), header) {
|
|
|
|
// No, we need to insert it here
|
|
|
|
parent.children.push(header.clone());
|
|
|
|
}
|
2019-08-24 20:23:08 +00:00
|
|
|
true
|
2019-08-15 08:19:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-08-15 08:19:32 +00:00
|
|
|
for header in headers {
|
2019-08-24 20:23:08 +00:00
|
|
|
// First header or we try to insert the current header in a previous one
|
|
|
|
if toc.is_empty() || !insert_into_parent(toc.iter_mut().last(), &header) {
|
2019-01-27 23:34:18 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-08-15 08:19:32 +00:00
|
|
|
#[test]
|
|
|
|
fn can_make_deep_toc() {
|
|
|
|
let input = vec![
|
|
|
|
Header::new(1),
|
|
|
|
Header::new(2),
|
|
|
|
Header::new(3),
|
|
|
|
Header::new(4),
|
|
|
|
Header::new(5),
|
|
|
|
Header::new(4),
|
|
|
|
];
|
|
|
|
let toc = make_table_of_contents(input);
|
|
|
|
assert_eq!(toc.len(), 1);
|
|
|
|
assert_eq!(toc[0].children.len(), 1);
|
|
|
|
assert_eq!(toc[0].children[0].children.len(), 1);
|
|
|
|
assert_eq!(toc[0].children[0].children[0].children.len(), 2);
|
|
|
|
assert_eq!(toc[0].children[0].children[0].children[0].children.len(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_make_deep_messy_toc() {
|
|
|
|
let input = vec![
|
|
|
|
Header::new(2), // toc[0]
|
|
|
|
Header::new(3),
|
|
|
|
Header::new(4),
|
|
|
|
Header::new(5),
|
|
|
|
Header::new(4),
|
|
|
|
Header::new(2), // toc[1]
|
|
|
|
Header::new(1), // toc[2]
|
|
|
|
Header::new(2),
|
|
|
|
Header::new(3),
|
|
|
|
Header::new(4),
|
|
|
|
];
|
|
|
|
let toc = make_table_of_contents(input);
|
|
|
|
assert_eq!(toc.len(), 3);
|
|
|
|
assert_eq!(toc[0].children.len(), 1);
|
|
|
|
assert_eq!(toc[0].children[0].children.len(), 2);
|
|
|
|
assert_eq!(toc[0].children[0].children[0].children.len(), 1);
|
|
|
|
assert_eq!(toc[1].children.len(), 0);
|
|
|
|
assert_eq!(toc[2].children.len(), 1);
|
|
|
|
assert_eq!(toc[2].children[0].children.len(), 1);
|
|
|
|
assert_eq!(toc[2].children[0].children[0].children.len(), 1);
|
|
|
|
}
|
|
|
|
|
2017-06-16 04:00:48 +00:00
|
|
|
#[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);
|
|
|
|
}
|
|
|
|
}
|