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.

193 lines
6.3KB

  1. use std::cmp::Ordering;
  2. use chrono::NaiveDateTime;
  3. use rayon::prelude::*;
  4. use slotmap::Key;
  5. use content::Page;
  6. /// Used by the RSS feed
  7. /// There to not have to import sorting stuff in the site crate
  8. #[allow(clippy::trivially_copy_pass_by_ref)]
  9. pub fn sort_actual_pages_by_date(a: &&Page, b: &&Page) -> Ordering {
  10. let ord = b.meta.datetime.unwrap().cmp(&a.meta.datetime.unwrap());
  11. if ord == Ordering::Equal {
  12. a.permalink.cmp(&b.permalink)
  13. } else {
  14. ord
  15. }
  16. }
  17. /// Takes a list of (page key, date, permalink) and sort them by dates if possible
  18. /// Pages without date will be put in the unsortable bucket
  19. /// The permalink is used to break ties
  20. pub fn sort_pages_by_date(pages: Vec<(&Key, Option<NaiveDateTime>, &str)>) -> (Vec<Key>, Vec<Key>) {
  21. let (mut can_be_sorted, cannot_be_sorted): (Vec<_>, Vec<_>) =
  22. pages.into_par_iter().partition(|page| page.1.is_some());
  23. can_be_sorted.par_sort_unstable_by(|a, b| {
  24. let ord = b.1.unwrap().cmp(&a.1.unwrap());
  25. if ord == Ordering::Equal {
  26. a.2.cmp(&b.2)
  27. } else {
  28. ord
  29. }
  30. });
  31. (can_be_sorted.iter().map(|p| *p.0).collect(), cannot_be_sorted.iter().map(|p| *p.0).collect())
  32. }
  33. /// Takes a list of (page key, weight, permalink) and sort them by weight if possible
  34. /// Pages without weight will be put in the unsortable bucket
  35. /// The permalink is used to break ties
  36. pub fn sort_pages_by_weight(pages: Vec<(&Key, Option<usize>, &str)>) -> (Vec<Key>, Vec<Key>) {
  37. let (mut can_be_sorted, cannot_be_sorted): (Vec<_>, Vec<_>) =
  38. pages.into_par_iter().partition(|page| page.1.is_some());
  39. can_be_sorted.par_sort_unstable_by(|a, b| {
  40. let ord = a.1.unwrap().cmp(&b.1.unwrap());
  41. if ord == Ordering::Equal {
  42. a.2.cmp(&b.2)
  43. } else {
  44. ord
  45. }
  46. });
  47. (can_be_sorted.iter().map(|p| *p.0).collect(), cannot_be_sorted.iter().map(|p| *p.0).collect())
  48. }
  49. /// Find the lighter/heavier and earlier/later pages for all pages having a date/weight
  50. pub fn find_siblings(sorted: &[Key]) -> Vec<(Key, Option<Key>, Option<Key>)> {
  51. let mut res = Vec::with_capacity(sorted.len());
  52. let length = sorted.len();
  53. for (i, key) in sorted.iter().enumerate() {
  54. let mut with_siblings = (*key, None, None);
  55. if i > 0 {
  56. // lighter / later
  57. with_siblings.1 = Some(sorted[i - 1]);
  58. }
  59. if i < length - 1 {
  60. // heavier/earlier
  61. with_siblings.2 = Some(sorted[i + 1]);
  62. }
  63. res.push(with_siblings);
  64. }
  65. res
  66. }
  67. #[cfg(test)]
  68. mod tests {
  69. use slotmap::DenseSlotMap;
  70. use std::path::PathBuf;
  71. use super::{find_siblings, sort_pages_by_date, sort_pages_by_weight};
  72. use content::Page;
  73. use front_matter::PageFrontMatter;
  74. fn create_page_with_date(date: &str) -> Page {
  75. let mut front_matter = PageFrontMatter::default();
  76. front_matter.date = Some(date.to_string());
  77. front_matter.date_to_datetime();
  78. Page::new("content/hello.md", front_matter, &PathBuf::new())
  79. }
  80. fn create_page_with_weight(weight: usize) -> Page {
  81. let mut front_matter = PageFrontMatter::default();
  82. front_matter.weight = Some(weight);
  83. Page::new("content/hello.md", front_matter, &PathBuf::new())
  84. }
  85. #[test]
  86. fn can_sort_by_dates() {
  87. let mut dense = DenseSlotMap::new();
  88. let page1 = create_page_with_date("2018-01-01");
  89. let key1 = dense.insert(page1.clone());
  90. let page2 = create_page_with_date("2017-01-01");
  91. let key2 = dense.insert(page2.clone());
  92. let page3 = create_page_with_date("2019-01-01");
  93. let key3 = dense.insert(page3.clone());
  94. let input = vec![
  95. (&key1, page1.meta.datetime, page1.permalink.as_ref()),
  96. (&key2, page2.meta.datetime, page2.permalink.as_ref()),
  97. (&key3, page3.meta.datetime, page3.permalink.as_ref()),
  98. ];
  99. let (pages, _) = sort_pages_by_date(input);
  100. // Should be sorted by date
  101. assert_eq!(pages[0], key3);
  102. assert_eq!(pages[1], key1);
  103. assert_eq!(pages[2], key2);
  104. }
  105. #[test]
  106. fn can_sort_by_weight() {
  107. let mut dense = DenseSlotMap::new();
  108. let page1 = create_page_with_weight(2);
  109. let key1 = dense.insert(page1.clone());
  110. let page2 = create_page_with_weight(3);
  111. let key2 = dense.insert(page2.clone());
  112. let page3 = create_page_with_weight(1);
  113. let key3 = dense.insert(page3.clone());
  114. let input = vec![
  115. (&key1, page1.meta.weight, page1.permalink.as_ref()),
  116. (&key2, page2.meta.weight, page2.permalink.as_ref()),
  117. (&key3, page3.meta.weight, page3.permalink.as_ref()),
  118. ];
  119. let (pages, _) = sort_pages_by_weight(input);
  120. // Should be sorted by weight
  121. assert_eq!(pages[0], key3);
  122. assert_eq!(pages[1], key1);
  123. assert_eq!(pages[2], key2);
  124. }
  125. #[test]
  126. fn ignore_page_with_missing_field() {
  127. let mut dense = DenseSlotMap::new();
  128. let page1 = create_page_with_weight(2);
  129. let key1 = dense.insert(page1.clone());
  130. let page2 = create_page_with_weight(3);
  131. let key2 = dense.insert(page2.clone());
  132. let page3 = create_page_with_date("2019-01-01");
  133. let key3 = dense.insert(page3.clone());
  134. let input = vec![
  135. (&key1, page1.meta.weight, page1.permalink.as_ref()),
  136. (&key2, page2.meta.weight, page2.permalink.as_ref()),
  137. (&key3, page3.meta.weight, page3.permalink.as_ref()),
  138. ];
  139. let (pages, unsorted) = sort_pages_by_weight(input);
  140. assert_eq!(pages.len(), 2);
  141. assert_eq!(unsorted.len(), 1);
  142. }
  143. #[test]
  144. fn can_find_siblings() {
  145. let mut dense = DenseSlotMap::new();
  146. let page1 = create_page_with_weight(1);
  147. let key1 = dense.insert(page1.clone());
  148. let page2 = create_page_with_weight(2);
  149. let key2 = dense.insert(page2.clone());
  150. let page3 = create_page_with_weight(3);
  151. let key3 = dense.insert(page3.clone());
  152. let input = vec![key1, key2, key3];
  153. let pages = find_siblings(&input);
  154. assert_eq!(pages[0].1, None);
  155. assert_eq!(pages[0].2, Some(key2));
  156. assert_eq!(pages[1].1, Some(key1));
  157. assert_eq!(pages[1].2, Some(key3));
  158. assert_eq!(pages[2].1, Some(key2));
  159. assert_eq!(pages[2].2, None);
  160. }
  161. }