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.

167 lines
4.9KB

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