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.

301 lines
9.9KB

  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 the 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::Order => page.meta.order.is_some(),
  19. SortBy::Weight => page.meta.weight.is_some(),
  20. _ => unreachable!()
  21. }
  22. });
  23. match sort_by {
  24. SortBy::Date => {
  25. can_be_sorted.par_sort_unstable_by(|a, b| {
  26. let ord = b.meta.date().unwrap().cmp(&a.meta.date().unwrap());
  27. if ord == Ordering::Equal {
  28. a.permalink.cmp(&b.permalink)
  29. } else {
  30. ord
  31. }
  32. })
  33. },
  34. SortBy::Order => {
  35. can_be_sorted.par_sort_unstable_by(|a, b| {
  36. let ord = b.meta.order().cmp(&a.meta.order());
  37. if ord == Ordering::Equal {
  38. a.permalink.cmp(&b.permalink)
  39. } else {
  40. ord
  41. }
  42. })
  43. },
  44. SortBy::Weight => {
  45. can_be_sorted.par_sort_unstable_by(|a, b| {
  46. let ord = a.meta.weight().cmp(&b.meta.weight());
  47. if ord == Ordering::Equal {
  48. a.permalink.cmp(&b.permalink)
  49. } else {
  50. ord
  51. }
  52. })
  53. },
  54. _ => unreachable!()
  55. };
  56. (can_be_sorted, cannot_be_sorted)
  57. }
  58. /// Horribly inefficient way to set previous and next on each pages that skips drafts
  59. /// So many clones
  60. pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec<Page> {
  61. let mut res = Vec::with_capacity(input.len());
  62. // The input is already sorted
  63. for (i, _) in input.iter().enumerate() {
  64. let mut new_page = input[i].clone();
  65. if new_page.is_draft() {
  66. res.push(new_page);
  67. continue;
  68. }
  69. if i > 0 {
  70. let mut j = i;
  71. loop {
  72. if j == 0 {
  73. break;
  74. }
  75. j -= 1;
  76. if input[j].is_draft() {
  77. continue;
  78. }
  79. // Remove prev/next otherwise we serialise the whole thing...
  80. let mut next_page = input[j].clone();
  81. next_page.previous = None;
  82. next_page.next = None;
  83. new_page.next = Some(Box::new(next_page));
  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. previous_page.previous = None;
  100. previous_page.next = None;
  101. new_page.previous = Some(Box::new(previous_page));
  102. break;
  103. }
  104. }
  105. res.push(new_page);
  106. }
  107. res
  108. }
  109. #[cfg(test)]
  110. mod tests {
  111. use front_matter::{PageFrontMatter, SortBy};
  112. use page::Page;
  113. use super::{sort_pages, populate_previous_and_next_pages};
  114. fn create_page_with_date(date: &str) -> Page {
  115. let mut front_matter = PageFrontMatter::default();
  116. front_matter.date = Some(date.to_string());
  117. Page::new("content/hello.md", front_matter)
  118. }
  119. fn create_page_with_order(order: usize, filename: &str) -> Page {
  120. let mut front_matter = PageFrontMatter::default();
  121. front_matter.order = Some(order);
  122. let mut p = Page::new("content/".to_string() + filename, front_matter);
  123. // Faking a permalink to test sorting with equal order
  124. p.permalink = filename.to_string();
  125. p
  126. }
  127. fn create_draft_page_with_order(order: usize) -> Page {
  128. let mut front_matter = PageFrontMatter::default();
  129. front_matter.order = Some(order);
  130. front_matter.draft = true;
  131. Page::new("content/hello.md", front_matter)
  132. }
  133. fn create_page_with_weight(weight: usize) -> Page {
  134. let mut front_matter = PageFrontMatter::default();
  135. front_matter.weight = Some(weight);
  136. Page::new("content/hello.md", front_matter)
  137. }
  138. #[test]
  139. fn can_sort_by_dates() {
  140. let input = vec![
  141. create_page_with_date("2018-01-01"),
  142. create_page_with_date("2017-01-01"),
  143. create_page_with_date("2019-01-01"),
  144. ];
  145. let (pages, _) = sort_pages(input, SortBy::Date);
  146. // Should be sorted by date
  147. assert_eq!(pages[0].clone().meta.date.unwrap().to_string(), "2019-01-01");
  148. assert_eq!(pages[1].clone().meta.date.unwrap().to_string(), "2018-01-01");
  149. assert_eq!(pages[2].clone().meta.date.unwrap().to_string(), "2017-01-01");
  150. }
  151. #[test]
  152. fn can_sort_by_order() {
  153. let input = vec![
  154. create_page_with_order(2, "hello.md"),
  155. create_page_with_order(3, "hello2.md"),
  156. create_page_with_order(1, "hello3.md"),
  157. ];
  158. let (pages, _) = sort_pages(input, SortBy::Order);
  159. // Should be sorted by order
  160. assert_eq!(pages[0].clone().meta.order.unwrap(), 3);
  161. assert_eq!(pages[1].clone().meta.order.unwrap(), 2);
  162. assert_eq!(pages[2].clone().meta.order.unwrap(), 1);
  163. }
  164. #[test]
  165. fn can_sort_by_order_uses_permalink_to_break_ties() {
  166. let input = vec![
  167. create_page_with_order(3, "b.md"),
  168. create_page_with_order(3, "a.md"),
  169. create_page_with_order(3, "c.md"),
  170. ];
  171. let (pages, _) = sort_pages(input, SortBy::Order);
  172. // Should be sorted by order
  173. assert_eq!(pages[0].clone().meta.order.unwrap(), 3);
  174. assert_eq!(pages[0].clone().permalink, "a.md");
  175. assert_eq!(pages[1].clone().meta.order.unwrap(), 3);
  176. assert_eq!(pages[1].clone().permalink, "b.md");
  177. assert_eq!(pages[2].clone().meta.order.unwrap(), 3);
  178. assert_eq!(pages[2].clone().permalink, "c.md");
  179. }
  180. #[test]
  181. fn can_sort_by_weight() {
  182. let input = vec![
  183. create_page_with_weight(2),
  184. create_page_with_weight(3),
  185. create_page_with_weight(1),
  186. ];
  187. let (pages, _) = sort_pages(input, SortBy::Weight);
  188. // Should be sorted by weight
  189. assert_eq!(pages[0].clone().meta.weight.unwrap(), 1);
  190. assert_eq!(pages[1].clone().meta.weight.unwrap(), 2);
  191. assert_eq!(pages[2].clone().meta.weight.unwrap(), 3);
  192. }
  193. #[test]
  194. fn can_sort_by_none() {
  195. let input = vec![
  196. create_page_with_order(2, "a.md"),
  197. create_page_with_order(3, "a.md"),
  198. create_page_with_order(1, "a.md"),
  199. ];
  200. let (pages, _) = sort_pages(input, SortBy::None);
  201. // Should be sorted by date
  202. assert_eq!(pages[0].clone().meta.order.unwrap(), 2);
  203. assert_eq!(pages[1].clone().meta.order.unwrap(), 3);
  204. assert_eq!(pages[2].clone().meta.order.unwrap(), 1);
  205. }
  206. #[test]
  207. fn ignore_page_with_missing_field() {
  208. let input = vec![
  209. create_page_with_order(2, "a.md"),
  210. create_page_with_order(3, "a.md"),
  211. create_page_with_date("2019-01-01"),
  212. ];
  213. let (pages, unsorted) = sort_pages(input, SortBy::Order);
  214. assert_eq!(pages.len(), 2);
  215. assert_eq!(unsorted.len(), 1);
  216. }
  217. #[test]
  218. fn can_populate_previous_and_next_pages() {
  219. let input = vec![
  220. create_page_with_order(1, "a.md"),
  221. create_page_with_order(2, "b.md"),
  222. create_page_with_order(3, "a.md"),
  223. ];
  224. let pages = populate_previous_and_next_pages(&input);
  225. assert!(pages[0].clone().next.is_none());
  226. assert!(pages[0].clone().previous.is_some());
  227. assert_eq!(pages[0].clone().previous.unwrap().meta.order.unwrap(), 2);
  228. assert!(pages[1].clone().next.is_some());
  229. assert!(pages[1].clone().previous.is_some());
  230. assert_eq!(pages[1].clone().previous.unwrap().meta.order.unwrap(), 3);
  231. assert_eq!(pages[1].clone().next.unwrap().meta.order.unwrap(), 1);
  232. assert!(pages[2].clone().next.is_some());
  233. assert!(pages[2].clone().previous.is_none());
  234. assert_eq!(pages[2].clone().next.unwrap().meta.order.unwrap(), 2);
  235. }
  236. #[test]
  237. fn can_populate_previous_and_next_pages_skip_drafts() {
  238. let input = vec![
  239. create_draft_page_with_order(0),
  240. create_page_with_order(1, "a.md"),
  241. create_page_with_order(2, "b.md"),
  242. create_page_with_order(3, "c.md"),
  243. create_draft_page_with_order(4),
  244. ];
  245. let pages = populate_previous_and_next_pages(&input);
  246. assert!(pages[0].clone().next.is_none());
  247. assert!(pages[0].clone().previous.is_none());
  248. assert!(pages[1].clone().next.is_none());
  249. assert!(pages[1].clone().previous.is_some());
  250. assert_eq!(pages[1].clone().previous.unwrap().meta.order.unwrap(), 2);
  251. assert!(pages[2].clone().next.is_some());
  252. assert!(pages[2].clone().previous.is_some());
  253. assert_eq!(pages[2].clone().previous.unwrap().meta.order.unwrap(), 3);
  254. assert_eq!(pages[2].clone().next.unwrap().meta.order.unwrap(), 1);
  255. assert!(pages[3].clone().next.is_some());
  256. assert!(pages[3].clone().previous.is_none());
  257. assert_eq!(pages[3].clone().next.unwrap().meta.order.unwrap(), 2);
  258. assert!(pages[4].clone().next.is_none());
  259. assert!(pages[4].clone().previous.is_none());
  260. }
  261. }