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.

225 lines
7.1KB

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