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.

199 lines
6.5KB

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