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.

252 lines
8.2KB

  1. use rayon::prelude::*;
  2. use page::Page;
  3. use front_matter::SortBy;
  4. /// Sort pages by the given criteria
  5. ///
  6. /// Any pages that doesn't have a the required field when the sorting method is other than none
  7. /// will be ignored.
  8. pub fn sort_pages(pages: Vec<Page>, sort_by: SortBy) -> (Vec<Page>, Vec<Page>) {
  9. if sort_by == SortBy::None {
  10. return (pages, vec![]);
  11. }
  12. let (mut can_be_sorted, cannot_be_sorted): (Vec<_>, Vec<_>) = pages
  13. .into_par_iter()
  14. .partition(|page| {
  15. match sort_by {
  16. SortBy::Date => page.meta.date.is_some(),
  17. SortBy::Order => page.meta.order.is_some(),
  18. SortBy::Weight => page.meta.weight.is_some(),
  19. _ => unreachable!()
  20. }
  21. });
  22. match sort_by {
  23. SortBy::Date => can_be_sorted.par_sort_unstable_by(|a, b| b.meta.date().unwrap().cmp(&a.meta.date().unwrap())),
  24. SortBy::Order => can_be_sorted.par_sort_unstable_by(|a, b| b.meta.order().cmp(&a.meta.order())),
  25. SortBy::Weight => can_be_sorted.par_sort_unstable_by(|a, b| a.meta.weight().cmp(&b.meta.weight())),
  26. _ => unreachable!()
  27. };
  28. (can_be_sorted, cannot_be_sorted)
  29. }
  30. /// Horribly inefficient way to set previous and next on each pages that skips drafts
  31. /// So many clones
  32. pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec<Page> {
  33. let mut res = Vec::with_capacity(input.len());
  34. // The input is already sorted
  35. for (i, _) in input.iter().enumerate() {
  36. let mut new_page = input[i].clone();
  37. if new_page.is_draft() {
  38. res.push(new_page);
  39. continue;
  40. }
  41. if i > 0 {
  42. let mut j = i;
  43. loop {
  44. if j == 0 {
  45. break;
  46. }
  47. j -= 1;
  48. if input[j].is_draft() {
  49. continue;
  50. }
  51. // Remove prev/next otherwise we serialise the whole thing...
  52. let mut next_page = input[j].clone();
  53. next_page.previous = None;
  54. next_page.next = None;
  55. new_page.next = Some(Box::new(next_page));
  56. break;
  57. }
  58. }
  59. if i < input.len() - 1 {
  60. let mut j = i;
  61. loop {
  62. if j == input.len() - 1 {
  63. break;
  64. }
  65. j += 1;
  66. if input[j].is_draft() {
  67. continue;
  68. }
  69. // Remove prev/next otherwise we serialise the whole thing...
  70. let mut previous_page = input[j].clone();
  71. previous_page.previous = None;
  72. previous_page.next = None;
  73. new_page.previous = Some(Box::new(previous_page));
  74. break;
  75. }
  76. }
  77. res.push(new_page);
  78. }
  79. res
  80. }
  81. #[cfg(test)]
  82. mod tests {
  83. use front_matter::{PageFrontMatter, SortBy};
  84. use page::Page;
  85. use super::{sort_pages, populate_previous_and_next_pages};
  86. fn create_page_with_date(date: &str) -> Page {
  87. let mut front_matter = PageFrontMatter::default();
  88. front_matter.date = Some(date.to_string());
  89. Page::new("content/hello.md", front_matter)
  90. }
  91. fn create_page_with_order(order: usize) -> Page {
  92. let mut front_matter = PageFrontMatter::default();
  93. front_matter.order = Some(order);
  94. Page::new("content/hello.md", front_matter)
  95. }
  96. fn create_draft_page_with_order(order: usize) -> Page {
  97. let mut front_matter = PageFrontMatter::default();
  98. front_matter.order = Some(order);
  99. front_matter.draft = Some(true);
  100. Page::new("content/hello.md", front_matter)
  101. }
  102. fn create_page_with_weight(weight: usize) -> Page {
  103. let mut front_matter = PageFrontMatter::default();
  104. front_matter.weight = Some(weight);
  105. Page::new("content/hello.md", front_matter)
  106. }
  107. #[test]
  108. fn can_sort_by_dates() {
  109. let input = vec![
  110. create_page_with_date("2018-01-01"),
  111. create_page_with_date("2017-01-01"),
  112. create_page_with_date("2019-01-01"),
  113. ];
  114. let (pages, _) = sort_pages(input, SortBy::Date);
  115. // Should be sorted by date
  116. assert_eq!(pages[0].clone().meta.date.unwrap(), "2019-01-01");
  117. assert_eq!(pages[1].clone().meta.date.unwrap(), "2018-01-01");
  118. assert_eq!(pages[2].clone().meta.date.unwrap(), "2017-01-01");
  119. }
  120. #[test]
  121. fn can_sort_by_order() {
  122. let input = vec![
  123. create_page_with_order(2),
  124. create_page_with_order(3),
  125. create_page_with_order(1),
  126. ];
  127. let (pages, _) = sort_pages(input, SortBy::Order);
  128. // Should be sorted by date
  129. assert_eq!(pages[0].clone().meta.order.unwrap(), 3);
  130. assert_eq!(pages[1].clone().meta.order.unwrap(), 2);
  131. assert_eq!(pages[2].clone().meta.order.unwrap(), 1);
  132. }
  133. #[test]
  134. fn can_sort_by_weight() {
  135. let input = vec![
  136. create_page_with_weight(2),
  137. create_page_with_weight(3),
  138. create_page_with_weight(1),
  139. ];
  140. let (pages, _) = sort_pages(input, SortBy::Weight);
  141. // Should be sorted by date
  142. assert_eq!(pages[0].clone().meta.weight.unwrap(), 1);
  143. assert_eq!(pages[1].clone().meta.weight.unwrap(), 2);
  144. assert_eq!(pages[2].clone().meta.weight.unwrap(), 3);
  145. }
  146. #[test]
  147. fn can_sort_by_none() {
  148. let input = vec![
  149. create_page_with_order(2),
  150. create_page_with_order(3),
  151. create_page_with_order(1),
  152. ];
  153. let (pages, _) = sort_pages(input, SortBy::None);
  154. // Should be sorted by date
  155. assert_eq!(pages[0].clone().meta.order.unwrap(), 2);
  156. assert_eq!(pages[1].clone().meta.order.unwrap(), 3);
  157. assert_eq!(pages[2].clone().meta.order.unwrap(), 1);
  158. }
  159. #[test]
  160. fn ignore_page_with_missing_field() {
  161. let input = vec![
  162. create_page_with_order(2),
  163. create_page_with_order(3),
  164. create_page_with_date("2019-01-01"),
  165. ];
  166. let (pages, unsorted) = sort_pages(input, SortBy::Order);
  167. assert_eq!(pages.len(), 2);
  168. assert_eq!(unsorted.len(), 1);
  169. }
  170. #[test]
  171. fn can_populate_previous_and_next_pages() {
  172. let input = vec![
  173. create_page_with_order(1),
  174. create_page_with_order(2),
  175. create_page_with_order(3),
  176. ];
  177. let pages = populate_previous_and_next_pages(&input);
  178. assert!(pages[0].clone().next.is_none());
  179. assert!(pages[0].clone().previous.is_some());
  180. assert_eq!(pages[0].clone().previous.unwrap().meta.order.unwrap(), 2);
  181. assert!(pages[1].clone().next.is_some());
  182. assert!(pages[1].clone().previous.is_some());
  183. assert_eq!(pages[1].clone().previous.unwrap().meta.order.unwrap(), 3);
  184. assert_eq!(pages[1].clone().next.unwrap().meta.order.unwrap(), 1);
  185. assert!(pages[2].clone().next.is_some());
  186. assert!(pages[2].clone().previous.is_none());
  187. assert_eq!(pages[2].clone().next.unwrap().meta.order.unwrap(), 2);
  188. }
  189. #[test]
  190. fn can_populate_previous_and_next_pages_skip_drafts() {
  191. let input = vec![
  192. create_draft_page_with_order(0),
  193. create_page_with_order(1),
  194. create_page_with_order(2),
  195. create_page_with_order(3),
  196. create_draft_page_with_order(4),
  197. ];
  198. let pages = populate_previous_and_next_pages(&input);
  199. assert!(pages[0].clone().next.is_none());
  200. assert!(pages[0].clone().previous.is_none());
  201. assert!(pages[1].clone().next.is_none());
  202. assert!(pages[1].clone().previous.is_some());
  203. assert_eq!(pages[1].clone().previous.unwrap().meta.order.unwrap(), 2);
  204. assert!(pages[2].clone().next.is_some());
  205. assert!(pages[2].clone().previous.is_some());
  206. assert_eq!(pages[2].clone().previous.unwrap().meta.order.unwrap(), 3);
  207. assert_eq!(pages[2].clone().next.unwrap().meta.order.unwrap(), 1);
  208. assert!(pages[3].clone().next.is_some());
  209. assert!(pages[3].clone().previous.is_none());
  210. assert_eq!(pages[3].clone().next.unwrap().meta.order.unwrap(), 2);
  211. assert!(pages[4].clone().next.is_none());
  212. assert!(pages[4].clone().previous.is_none());
  213. }
  214. }