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.2KB

  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_siblings(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. break;
  84. }
  85. }
  86. if i < input.len() - 1 {
  87. let mut j = i;
  88. loop {
  89. if j == input.len() - 1 {
  90. break;
  91. }
  92. j += 1;
  93. if input[j].is_draft() {
  94. continue;
  95. }
  96. // Remove prev/next otherwise we serialise the whole thing...
  97. let mut previous_page = input[j].clone();
  98. match sort_by {
  99. SortBy::Weight => {
  100. previous_page.lighter = None;
  101. previous_page.heavier = None;
  102. new_page.heavier = Some(Box::new(previous_page));
  103. }
  104. SortBy::Date => {
  105. previous_page.earlier = None;
  106. previous_page.later = None;
  107. new_page.earlier = Some(Box::new(previous_page));
  108. }
  109. SortBy::None => {}
  110. }
  111. break;
  112. }
  113. }
  114. res.push(new_page);
  115. }
  116. res
  117. }
  118. #[cfg(test)]
  119. mod tests {
  120. use front_matter::{PageFrontMatter, SortBy};
  121. use page::Page;
  122. use super::{sort_pages, populate_siblings};
  123. fn create_page_with_date(date: &str) -> Page {
  124. let mut front_matter = PageFrontMatter::default();
  125. front_matter.date = Some(date.to_string());
  126. Page::new("content/hello.md", front_matter)
  127. }
  128. fn create_page_with_weight(weight: usize) -> Page {
  129. let mut front_matter = PageFrontMatter::default();
  130. front_matter.weight = Some(weight);
  131. Page::new("content/hello.md", front_matter)
  132. }
  133. #[test]
  134. fn can_sort_by_dates() {
  135. let input = vec![
  136. create_page_with_date("2018-01-01"),
  137. create_page_with_date("2017-01-01"),
  138. create_page_with_date("2019-01-01"),
  139. ];
  140. let (pages, _) = sort_pages(input, SortBy::Date);
  141. // Should be sorted by date
  142. assert_eq!(pages[0].clone().meta.date.unwrap().to_string(), "2019-01-01");
  143. assert_eq!(pages[1].clone().meta.date.unwrap().to_string(), "2018-01-01");
  144. assert_eq!(pages[2].clone().meta.date.unwrap().to_string(), "2017-01-01");
  145. }
  146. #[test]
  147. fn can_sort_by_weight() {
  148. let input = vec![
  149. create_page_with_weight(2),
  150. create_page_with_weight(3),
  151. create_page_with_weight(1),
  152. ];
  153. let (pages, _) = sort_pages(input, SortBy::Weight);
  154. // Should be sorted by weight
  155. assert_eq!(pages[0].clone().meta.weight.unwrap(), 1);
  156. assert_eq!(pages[1].clone().meta.weight.unwrap(), 2);
  157. assert_eq!(pages[2].clone().meta.weight.unwrap(), 3);
  158. }
  159. #[test]
  160. fn can_sort_by_none() {
  161. let input = vec![
  162. create_page_with_weight(2),
  163. create_page_with_weight(3),
  164. create_page_with_weight(1),
  165. ];
  166. let (pages, _) = sort_pages(input, SortBy::None);
  167. assert_eq!(pages[0].clone().meta.weight.unwrap(), 2);
  168. assert_eq!(pages[1].clone().meta.weight.unwrap(), 3);
  169. assert_eq!(pages[2].clone().meta.weight.unwrap(), 1);
  170. }
  171. #[test]
  172. fn ignore_page_with_missing_field() {
  173. let input = vec![
  174. create_page_with_weight(2),
  175. create_page_with_weight(3),
  176. create_page_with_date("2019-01-01"),
  177. ];
  178. let (pages, unsorted) = sort_pages(input, SortBy::Weight);
  179. assert_eq!(pages.len(), 2);
  180. assert_eq!(unsorted.len(), 1);
  181. }
  182. #[test]
  183. fn can_populate_siblings() {
  184. let input = vec![
  185. create_page_with_weight(1),
  186. create_page_with_weight(2),
  187. create_page_with_weight(3),
  188. ];
  189. let pages = populate_siblings(&input, SortBy::Weight);
  190. assert!(pages[0].clone().lighter.is_none());
  191. assert!(pages[0].clone().heavier.is_some());
  192. assert_eq!(pages[0].clone().heavier.unwrap().meta.weight.unwrap(), 2);
  193. assert!(pages[1].clone().heavier.is_some());
  194. assert!(pages[1].clone().lighter.is_some());
  195. assert_eq!(pages[1].clone().lighter.unwrap().meta.weight.unwrap(), 1);
  196. assert_eq!(pages[1].clone().heavier.unwrap().meta.weight.unwrap(), 3);
  197. assert!(pages[2].clone().lighter.is_some());
  198. assert!(pages[2].clone().heavier.is_none());
  199. assert_eq!(pages[2].clone().lighter.unwrap().meta.weight.unwrap(), 2);
  200. }
  201. }