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.

255 lines
8.4KB

  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 std::str::FromStr;
  84. use toml::value::Datetime;
  85. use front_matter::{PageFrontMatter, SortBy};
  86. use page::Page;
  87. use super::{sort_pages, populate_previous_and_next_pages};
  88. fn create_page_with_date(date: &str) -> Page {
  89. let mut front_matter = PageFrontMatter::default();
  90. front_matter.date = Some(Datetime::from_str(date).unwrap());
  91. Page::new("content/hello.md", front_matter)
  92. }
  93. fn create_page_with_order(order: usize) -> Page {
  94. let mut front_matter = PageFrontMatter::default();
  95. front_matter.order = Some(order);
  96. Page::new("content/hello.md", front_matter)
  97. }
  98. fn create_draft_page_with_order(order: usize) -> Page {
  99. let mut front_matter = PageFrontMatter::default();
  100. front_matter.order = Some(order);
  101. front_matter.draft = Some(true);
  102. Page::new("content/hello.md", front_matter)
  103. }
  104. fn create_page_with_weight(weight: usize) -> Page {
  105. let mut front_matter = PageFrontMatter::default();
  106. front_matter.weight = Some(weight);
  107. Page::new("content/hello.md", front_matter)
  108. }
  109. #[test]
  110. fn can_sort_by_dates() {
  111. let input = vec![
  112. create_page_with_date("2018-01-01"),
  113. create_page_with_date("2017-01-01"),
  114. create_page_with_date("2019-01-01"),
  115. ];
  116. let (pages, _) = sort_pages(input, SortBy::Date);
  117. // Should be sorted by date
  118. assert_eq!(pages[0].clone().meta.date.unwrap().to_string(), "2019-01-01");
  119. assert_eq!(pages[1].clone().meta.date.unwrap().to_string(), "2018-01-01");
  120. assert_eq!(pages[2].clone().meta.date.unwrap().to_string(), "2017-01-01");
  121. }
  122. #[test]
  123. fn can_sort_by_order() {
  124. let input = vec![
  125. create_page_with_order(2),
  126. create_page_with_order(3),
  127. create_page_with_order(1),
  128. ];
  129. let (pages, _) = sort_pages(input, SortBy::Order);
  130. // Should be sorted by date
  131. assert_eq!(pages[0].clone().meta.order.unwrap(), 3);
  132. assert_eq!(pages[1].clone().meta.order.unwrap(), 2);
  133. assert_eq!(pages[2].clone().meta.order.unwrap(), 1);
  134. }
  135. #[test]
  136. fn can_sort_by_weight() {
  137. let input = vec![
  138. create_page_with_weight(2),
  139. create_page_with_weight(3),
  140. create_page_with_weight(1),
  141. ];
  142. let (pages, _) = sort_pages(input, SortBy::Weight);
  143. // Should be sorted by date
  144. assert_eq!(pages[0].clone().meta.weight.unwrap(), 1);
  145. assert_eq!(pages[1].clone().meta.weight.unwrap(), 2);
  146. assert_eq!(pages[2].clone().meta.weight.unwrap(), 3);
  147. }
  148. #[test]
  149. fn can_sort_by_none() {
  150. let input = vec![
  151. create_page_with_order(2),
  152. create_page_with_order(3),
  153. create_page_with_order(1),
  154. ];
  155. let (pages, _) = sort_pages(input, SortBy::None);
  156. // Should be sorted by date
  157. assert_eq!(pages[0].clone().meta.order.unwrap(), 2);
  158. assert_eq!(pages[1].clone().meta.order.unwrap(), 3);
  159. assert_eq!(pages[2].clone().meta.order.unwrap(), 1);
  160. }
  161. #[test]
  162. fn ignore_page_with_missing_field() {
  163. let input = vec![
  164. create_page_with_order(2),
  165. create_page_with_order(3),
  166. create_page_with_date("2019-01-01"),
  167. ];
  168. let (pages, unsorted) = sort_pages(input, SortBy::Order);
  169. assert_eq!(pages.len(), 2);
  170. assert_eq!(unsorted.len(), 1);
  171. }
  172. #[test]
  173. fn can_populate_previous_and_next_pages() {
  174. let input = vec![
  175. create_page_with_order(1),
  176. create_page_with_order(2),
  177. create_page_with_order(3),
  178. ];
  179. let pages = populate_previous_and_next_pages(&input);
  180. assert!(pages[0].clone().next.is_none());
  181. assert!(pages[0].clone().previous.is_some());
  182. assert_eq!(pages[0].clone().previous.unwrap().meta.order.unwrap(), 2);
  183. assert!(pages[1].clone().next.is_some());
  184. assert!(pages[1].clone().previous.is_some());
  185. assert_eq!(pages[1].clone().previous.unwrap().meta.order.unwrap(), 3);
  186. assert_eq!(pages[1].clone().next.unwrap().meta.order.unwrap(), 1);
  187. assert!(pages[2].clone().next.is_some());
  188. assert!(pages[2].clone().previous.is_none());
  189. assert_eq!(pages[2].clone().next.unwrap().meta.order.unwrap(), 2);
  190. }
  191. #[test]
  192. fn can_populate_previous_and_next_pages_skip_drafts() {
  193. let input = vec![
  194. create_draft_page_with_order(0),
  195. create_page_with_order(1),
  196. create_page_with_order(2),
  197. create_page_with_order(3),
  198. create_draft_page_with_order(4),
  199. ];
  200. let pages = populate_previous_and_next_pages(&input);
  201. assert!(pages[0].clone().next.is_none());
  202. assert!(pages[0].clone().previous.is_none());
  203. assert!(pages[1].clone().next.is_none());
  204. assert!(pages[1].clone().previous.is_some());
  205. assert_eq!(pages[1].clone().previous.unwrap().meta.order.unwrap(), 2);
  206. assert!(pages[2].clone().next.is_some());
  207. assert!(pages[2].clone().previous.is_some());
  208. assert_eq!(pages[2].clone().previous.unwrap().meta.order.unwrap(), 3);
  209. assert_eq!(pages[2].clone().next.unwrap().meta.order.unwrap(), 1);
  210. assert!(pages[3].clone().next.is_some());
  211. assert!(pages[3].clone().previous.is_none());
  212. assert_eq!(pages[3].clone().next.unwrap().meta.order.unwrap(), 2);
  213. assert!(pages[4].clone().next.is_none());
  214. assert!(pages[4].clone().previous.is_none());
  215. }
  216. }