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.

248 lines
7.7KB

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