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.

232 lines
7.1KB

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