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.

235 lines
7.4KB

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