You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
4.9KB

  1. /// Populated while receiving events from the markdown parser
  2. #[derive(Debug, PartialEq, Clone, Serialize)]
  3. pub struct Heading {
  4. #[serde(skip_serializing)]
  5. pub level: u32,
  6. pub id: String,
  7. pub permalink: String,
  8. pub title: String,
  9. pub children: Vec<Heading>,
  10. }
  11. impl Heading {
  12. pub fn new(level: u32) -> Heading {
  13. Heading {
  14. level,
  15. id: String::new(),
  16. permalink: String::new(),
  17. title: String::new(),
  18. children: Vec::new(),
  19. }
  20. }
  21. }
  22. impl Default for Heading {
  23. fn default() -> Self {
  24. Heading::new(0)
  25. }
  26. }
  27. // Takes a potential (mutable) parent and a heading to try and insert into
  28. // Returns true when it performed the insertion, false otherwise
  29. fn insert_into_parent(potential_parent: Option<&mut Heading>, heading: &Heading) -> bool {
  30. match potential_parent {
  31. None => {
  32. // No potential parent to insert into so it needs to be insert higher
  33. false
  34. }
  35. Some(parent) => {
  36. if heading.level <= parent.level {
  37. // Heading is same level or higher so we don't insert here
  38. return false;
  39. }
  40. if heading.level + 1 == parent.level {
  41. // We have a direct child of the parent
  42. parent.children.push(heading.clone());
  43. return true;
  44. }
  45. // We need to go deeper
  46. if !insert_into_parent(parent.children.iter_mut().last(), heading) {
  47. // No, we need to insert it here
  48. parent.children.push(heading.clone());
  49. }
  50. true
  51. }
  52. }
  53. }
  54. /// Converts the flat temp headings into a nested set of headings
  55. /// representing the hierarchy
  56. pub fn make_table_of_contents(headings: Vec<Heading>) -> Vec<Heading> {
  57. let mut toc = vec![];
  58. for heading in headings {
  59. // First heading or we try to insert the current heading in a previous one
  60. if toc.is_empty() || !insert_into_parent(toc.iter_mut().last(), &heading) {
  61. toc.push(heading);
  62. }
  63. }
  64. toc
  65. }
  66. #[cfg(test)]
  67. mod tests {
  68. use super::*;
  69. #[test]
  70. fn can_make_basic_toc() {
  71. let input = vec![Heading::new(1), Heading::new(1), Heading::new(1)];
  72. let toc = make_table_of_contents(input);
  73. assert_eq!(toc.len(), 3);
  74. }
  75. #[test]
  76. fn can_make_more_complex_toc() {
  77. let input = vec![
  78. Heading::new(1),
  79. Heading::new(2),
  80. Heading::new(2),
  81. Heading::new(3),
  82. Heading::new(2),
  83. Heading::new(1),
  84. Heading::new(2),
  85. Heading::new(3),
  86. Heading::new(3),
  87. ];
  88. let toc = make_table_of_contents(input);
  89. assert_eq!(toc.len(), 2);
  90. assert_eq!(toc[0].children.len(), 3);
  91. assert_eq!(toc[1].children.len(), 1);
  92. assert_eq!(toc[0].children[1].children.len(), 1);
  93. assert_eq!(toc[1].children[0].children.len(), 2);
  94. }
  95. #[test]
  96. fn can_make_deep_toc() {
  97. let input = vec![
  98. Heading::new(1),
  99. Heading::new(2),
  100. Heading::new(3),
  101. Heading::new(4),
  102. Heading::new(5),
  103. Heading::new(4),
  104. ];
  105. let toc = make_table_of_contents(input);
  106. assert_eq!(toc.len(), 1);
  107. assert_eq!(toc[0].children.len(), 1);
  108. assert_eq!(toc[0].children[0].children.len(), 1);
  109. assert_eq!(toc[0].children[0].children[0].children.len(), 2);
  110. assert_eq!(toc[0].children[0].children[0].children[0].children.len(), 1);
  111. }
  112. #[test]
  113. fn can_make_deep_messy_toc() {
  114. let input = vec![
  115. Heading::new(2), // toc[0]
  116. Heading::new(3),
  117. Heading::new(4),
  118. Heading::new(5),
  119. Heading::new(4),
  120. Heading::new(2), // toc[1]
  121. Heading::new(1), // toc[2]
  122. Heading::new(2),
  123. Heading::new(3),
  124. Heading::new(4),
  125. ];
  126. let toc = make_table_of_contents(input);
  127. assert_eq!(toc.len(), 3);
  128. assert_eq!(toc[0].children.len(), 1);
  129. assert_eq!(toc[0].children[0].children.len(), 2);
  130. assert_eq!(toc[0].children[0].children[0].children.len(), 1);
  131. assert_eq!(toc[1].children.len(), 0);
  132. assert_eq!(toc[2].children.len(), 1);
  133. assert_eq!(toc[2].children[0].children.len(), 1);
  134. assert_eq!(toc[2].children[0].children[0].children.len(), 1);
  135. }
  136. #[test]
  137. fn can_make_messy_toc() {
  138. let input = vec![
  139. Heading::new(3),
  140. Heading::new(2),
  141. Heading::new(2),
  142. Heading::new(3),
  143. Heading::new(2),
  144. Heading::new(1),
  145. Heading::new(4),
  146. ];
  147. let toc = make_table_of_contents(input);
  148. println!("{:#?}", toc);
  149. assert_eq!(toc.len(), 5);
  150. assert_eq!(toc[2].children.len(), 1);
  151. assert_eq!(toc[4].children.len(), 1);
  152. }
  153. }