zola/components/content/src/sorting.rs

232 lines
7.2 KiB
Rust
Raw Normal View History

use std::cmp::Ordering;
2017-07-01 03:06:49 +00:00
use rayon::prelude::*;
2017-07-01 07:47:41 +00:00
use page::Page;
use front_matter::SortBy;
2017-05-15 03:23:19 +00:00
2017-07-01 10:13:21 +00:00
/// Sort pages by the given criteria
2017-05-15 03:23:19 +00:00
///
/// Any pages that doesn't have a required field when the sorting method is other than none
2017-07-01 10:13:21 +00:00
/// will be ignored.
2017-05-15 03:23:19 +00:00
pub fn sort_pages(pages: Vec<Page>, sort_by: SortBy) -> (Vec<Page>, Vec<Page>) {
2017-07-01 10:13:21 +00:00
if sort_by == SortBy::None {
2018-07-31 13:17:31 +00:00
return (pages, vec![]);
2017-07-01 10:13:21 +00:00
}
let (mut can_be_sorted, cannot_be_sorted): (Vec<_>, Vec<_>) = pages
.into_par_iter()
2017-07-11 13:51:02 +00:00
.partition(|page| {
2017-07-01 10:13:21 +00:00
match sort_by {
SortBy::Date => page.meta.date.is_some(),
SortBy::Weight => page.meta.weight.is_some(),
_ => unreachable!()
2017-06-29 07:11:15 +00:00
}
2017-07-01 10:13:21 +00:00
});
2017-05-15 03:23:19 +00:00
2017-07-01 10:13:21 +00:00
match sort_by {
SortBy::Date => {
can_be_sorted.par_sort_unstable_by(|a, b| {
let ord = b.meta.date().unwrap().cmp(&a.meta.date().unwrap());
if ord == Ordering::Equal {
a.permalink.cmp(&b.permalink)
} else {
ord
}
})
2018-07-31 13:17:31 +00:00
}
SortBy::Weight => {
can_be_sorted.par_sort_unstable_by(|a, b| {
let ord = a.meta.weight().cmp(&b.meta.weight());
if ord == Ordering::Equal {
a.permalink.cmp(&b.permalink)
} else {
ord
}
})
2018-07-31 13:17:31 +00:00
}
2017-07-01 10:13:21 +00:00
_ => unreachable!()
};
(can_be_sorted, cannot_be_sorted)
2017-05-15 03:23:19 +00:00
}
/// Horribly inefficient way to set previous and next on each pages that skips drafts
2017-05-15 03:23:19 +00:00
/// So many clones
pub fn populate_siblings(input: &[Page], sort_by: SortBy) -> Vec<Page> {
2017-07-01 10:13:21 +00:00
let mut res = Vec::with_capacity(input.len());
2017-05-15 03:23:19 +00:00
2017-07-01 10:13:21 +00:00
// The input is already sorted
for (i, _) in input.iter().enumerate() {
let mut new_page = input[i].clone();
2017-05-15 03:23:19 +00:00
2017-09-25 09:55:43 +00:00
if new_page.is_draft() {
res.push(new_page);
continue;
}
2017-05-15 03:23:19 +00:00
if i > 0 {
2017-09-25 09:55:43 +00:00
let mut j = i;
loop {
if j == 0 {
break;
}
j -= 1;
if input[j].is_draft() {
continue;
}
// Remove prev/next otherwise we serialise the whole thing...
let mut next_page = input[j].clone();
match sort_by {
SortBy::Weight => {
next_page.lighter = None;
next_page.heavier = None;
new_page.lighter = Some(Box::new(next_page));
2018-07-31 13:17:31 +00:00
}
SortBy::Date => {
next_page.earlier = None;
next_page.later = None;
new_page.later = Some(Box::new(next_page));
2018-07-31 13:17:31 +00:00
}
SortBy::None => ()
}
2017-09-25 09:55:43 +00:00
break;
}
2017-05-15 03:23:19 +00:00
}
if i < input.len() - 1 {
2017-09-25 09:55:43 +00:00
let mut j = i;
loop {
if j == input.len() - 1 {
break;
}
j += 1;
if input[j].is_draft() {
continue;
}
// Remove prev/next otherwise we serialise the whole thing...
let mut previous_page = input[j].clone();
match sort_by {
SortBy::Weight => {
previous_page.lighter = None;
previous_page.heavier = None;
new_page.heavier = Some(Box::new(previous_page));
2018-07-31 13:17:31 +00:00
}
SortBy::Date => {
previous_page.earlier = None;
previous_page.later = None;
new_page.earlier = Some(Box::new(previous_page));
}
2018-07-31 13:17:31 +00:00
SortBy::None => {}
}
2017-09-25 09:55:43 +00:00
break;
}
2017-05-15 03:23:19 +00:00
}
res.push(new_page);
}
res
}
#[cfg(test)]
mod tests {
2017-07-01 07:47:41 +00:00
use front_matter::{PageFrontMatter, SortBy};
use page::Page;
use super::{sort_pages, populate_siblings};
2017-05-15 03:23:19 +00:00
fn create_page_with_date(date: &str) -> Page {
let mut front_matter = PageFrontMatter::default();
front_matter.date = Some(date.to_string());
Page::new("content/hello.md", front_matter)
2017-05-15 03:23:19 +00:00
}
2017-07-01 10:13:21 +00:00
fn create_page_with_weight(weight: usize) -> Page {
let mut front_matter = PageFrontMatter::default();
front_matter.weight = Some(weight);
Page::new("content/hello.md", front_matter)
}
2017-05-15 03:23:19 +00:00
#[test]
fn can_sort_by_dates() {
let input = vec![
create_page_with_date("2018-01-01"),
create_page_with_date("2017-01-01"),
create_page_with_date("2019-01-01"),
];
let (pages, _) = sort_pages(input, SortBy::Date);
// Should be sorted by date
assert_eq!(pages[0].clone().meta.date.unwrap().to_string(), "2019-01-01");
assert_eq!(pages[1].clone().meta.date.unwrap().to_string(), "2018-01-01");
assert_eq!(pages[2].clone().meta.date.unwrap().to_string(), "2017-01-01");
2017-05-15 03:23:19 +00:00
}
2017-06-29 07:11:15 +00:00
#[test]
fn can_sort_by_weight() {
let input = vec![
2017-07-01 10:13:21 +00:00
create_page_with_weight(2),
create_page_with_weight(3),
create_page_with_weight(1),
2017-06-29 07:11:15 +00:00
];
let (pages, _) = sort_pages(input, SortBy::Weight);
// Should be sorted by weight
2017-07-01 10:13:21 +00:00
assert_eq!(pages[0].clone().meta.weight.unwrap(), 1);
assert_eq!(pages[1].clone().meta.weight.unwrap(), 2);
assert_eq!(pages[2].clone().meta.weight.unwrap(), 3);
2017-06-29 07:11:15 +00:00
}
2017-05-15 03:23:19 +00:00
#[test]
fn can_sort_by_none() {
let input = vec![
create_page_with_weight(2),
create_page_with_weight(3),
create_page_with_weight(1),
2017-05-15 03:23:19 +00:00
];
let (pages, _) = sort_pages(input, SortBy::None);
assert_eq!(pages[0].clone().meta.weight.unwrap(), 2);
assert_eq!(pages[1].clone().meta.weight.unwrap(), 3);
assert_eq!(pages[2].clone().meta.weight.unwrap(), 1);
2017-05-15 03:23:19 +00:00
}
#[test]
fn ignore_page_with_missing_field() {
let input = vec![
create_page_with_weight(2),
create_page_with_weight(3),
2017-05-15 03:23:19 +00:00
create_page_with_date("2019-01-01"),
];
let (pages, unsorted) = sort_pages(input, SortBy::Weight);
2017-05-15 03:23:19 +00:00
assert_eq!(pages.len(), 2);
assert_eq!(unsorted.len(), 1);
}
#[test]
fn can_populate_siblings() {
2017-05-15 03:23:19 +00:00
let input = vec![
create_page_with_weight(1),
create_page_with_weight(2),
create_page_with_weight(3),
2017-09-25 09:55:43 +00:00
];
let pages = populate_siblings(&input, SortBy::Weight);
2017-09-25 09:55:43 +00:00
assert!(pages[0].clone().lighter.is_none());
assert!(pages[0].clone().heavier.is_some());
assert_eq!(pages[0].clone().heavier.unwrap().meta.weight.unwrap(), 2);
2017-09-25 09:55:43 +00:00
assert!(pages[1].clone().heavier.is_some());
assert!(pages[1].clone().lighter.is_some());
assert_eq!(pages[1].clone().lighter.unwrap().meta.weight.unwrap(), 1);
assert_eq!(pages[1].clone().heavier.unwrap().meta.weight.unwrap(), 3);
2017-09-25 09:55:43 +00:00
assert!(pages[2].clone().lighter.is_some());
assert!(pages[2].clone().heavier.is_none());
assert_eq!(pages[2].clone().lighter.unwrap().meta.weight.unwrap(), 2);
2017-09-25 09:55:43 +00:00
}
2017-05-15 03:23:19 +00:00
}