Add new sorting: UpdateDate that sorts by meta.updated. (#1452)

* Add new sorting: UpdateDate that sorts by `meta.updated`.

* Use `max(created, updated)` for sort-by-update-date

This prevents created but never updated articles from appearing at the end/not
appearing at all.
This commit is contained in:
Waffle Lapkin 2021-05-15 21:16:55 +03:00 committed by GitHub
parent b244bcdfbb
commit f8c6ea2b00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 35 deletions

View file

@ -48,6 +48,8 @@ impl RawFrontMatter<'_> {
pub enum SortBy {
/// Most recent to oldest
Date,
/// Most recent to oldest
UpdateDate,
/// Sort by title
Title,
/// Lower weight comes first

View file

@ -20,6 +20,12 @@ pub struct PageFrontMatter {
/// Updated date
#[serde(default, deserialize_with = "from_toml_datetime")]
pub updated: Option<String>,
/// Chrono converted update datatime
#[serde(default, skip_deserializing)]
pub updated_datetime: Option<NaiveDateTime>,
/// The converted update datetime into a (year, month, day) tuple
#[serde(default, skip_deserializing)]
pub updated_datetime_tuple: Option<(i32, u32, u32)>,
/// Date if we want to order pages (ie blog post)
#[serde(default, deserialize_with = "from_toml_datetime")]
pub date: Option<String>,
@ -107,6 +113,10 @@ impl PageFrontMatter {
pub fn date_to_datetime(&mut self) {
self.datetime = self.date.as_ref().map(|s| s.as_ref()).and_then(parse_datetime);
self.datetime_tuple = self.datetime.map(|dt| (dt.year(), dt.month(), dt.day()));
self.updated_datetime = self.updated.as_ref().map(|s| s.as_ref()).and_then(parse_datetime);
self.updated_datetime_tuple =
self.updated_datetime.map(|dt| (dt.year(), dt.month(), dt.day()));
}
pub fn weight(&self) -> usize {
@ -120,6 +130,8 @@ impl Default for PageFrontMatter {
title: None,
description: None,
updated: None,
updated_datetime: None,
updated_datetime_tuple: None,
date: None,
datetime: None,
datetime_tuple: None,

View file

@ -60,6 +60,10 @@ pub struct Page {
/// When <!-- more --> is found in the text, will take the content up to that part
/// as summary
pub summary: Option<String>,
/// The earlier updated page, for pages sorted by updated date
pub earlier_updated: Option<DefaultKey>,
/// The later updated page, for pages sorted by updated date
pub later_updated: Option<DefaultKey>,
/// The earlier page, for pages sorted by date
pub earlier: Option<DefaultKey>,
/// The later page, for pages sorted by date

View file

@ -90,6 +90,8 @@ pub struct SerializingPage<'a> {
lang: &'a str,
lighter: Option<Box<SerializingPage<'a>>>,
heavier: Option<Box<SerializingPage<'a>>>,
earlier_updated: Option<Box<SerializingPage<'a>>>,
later_updated: Option<Box<SerializingPage<'a>>>,
earlier: Option<Box<SerializingPage<'a>>>,
later: Option<Box<SerializingPage<'a>>>,
title_prev: Option<Box<SerializingPage<'a>>>,
@ -115,6 +117,12 @@ impl<'a> SerializingPage<'a> {
let heavier = page
.heavier
.map(|k| Box::new(Self::from_page_basic(pages.get(k).unwrap(), Some(library))));
let earlier_updated = page
.earlier_updated
.map(|k| Box::new(Self::from_page_basic(pages.get(k).unwrap(), Some(library))));
let later_updated = page
.later_updated
.map(|k| Box::new(Self::from_page_basic(pages.get(k).unwrap(), Some(library))));
let earlier = page
.earlier
.map(|k| Box::new(Self::from_page_basic(pages.get(k).unwrap(), Some(library))));
@ -161,6 +169,8 @@ impl<'a> SerializingPage<'a> {
lang: &page.lang,
lighter,
heavier,
earlier_updated,
later_updated,
earlier,
later,
title_prev,
@ -225,6 +235,8 @@ impl<'a> SerializingPage<'a> {
lang: &page.lang,
lighter: None,
heavier: None,
earlier_updated: None,
later_updated: None,
earlier: None,
later: None,
title_prev: None,

View file

@ -3,13 +3,12 @@ use std::path::{Path, PathBuf};
use slotmap::{DefaultKey, DenseSlotMap};
use front_matter::SortBy;
use crate::content::{Page, Section};
use crate::sorting::{
find_siblings, sort_pages_by_date, sort_pages_by_title, sort_pages_by_weight,
};
use config::Config;
use front_matter::{PageFrontMatter, SortBy};
// Like vec! but for HashSet
macro_rules! set {
@ -265,52 +264,47 @@ impl Library {
/// Sort all sections pages according to sorting method given
/// Pages that cannot be sorted are set to the section.ignored_pages instead
pub fn sort_sections_pages(&mut self) {
fn get_data<'a, T>(
section: &'a Section,
pages: &'a DenseSlotMap<DefaultKey, Page>,
field: impl Fn(&'a PageFrontMatter) -> Option<T>,
) -> Vec<(&'a DefaultKey, Option<T>, &'a str)> {
section
.pages
.iter()
.map(|k| {
if let Some(page) = pages.get(*k) {
(k, field(&page.meta), page.permalink.as_ref())
} else {
unreachable!("Sorting got an unknown page")
}
})
.collect()
}
let mut updates = HashMap::new();
for (key, section) in &self.sections {
let (sorted_pages, cannot_be_sorted_pages) = match section.meta.sort_by {
SortBy::None => continue,
SortBy::Date => {
let data = section
.pages
.iter()
.map(|k| {
if let Some(page) = self.pages.get(*k) {
(k, page.meta.datetime, page.permalink.as_ref())
} else {
unreachable!("Sorting got an unknown page")
let data = get_data(section, &self.pages, |meta| meta.datetime);
sort_pages_by_date(data)
}
})
.collect();
SortBy::UpdateDate => {
let data = get_data(section, &self.pages, |meta| {
std::cmp::max(meta.datetime, meta.updated_datetime)
});
sort_pages_by_date(data)
}
SortBy::Title => {
let data = section
.pages
.iter()
.map(|k| {
if let Some(page) = self.pages.get(*k) {
(k, page.meta.title.as_deref(), page.permalink.as_ref())
} else {
unreachable!("Sorting got an unknown page")
}
})
.collect();
let data = get_data(section, &self.pages, |meta| meta.title.as_deref());
sort_pages_by_title(data)
}
SortBy::Weight => {
let data = section
.pages
.iter()
.map(|k| {
if let Some(page) = self.pages.get(*k) {
(k, page.meta.weight, page.permalink.as_ref())
} else {
unreachable!("Sorting got an unknown page")
}
})
.collect();
let data = get_data(section, &self.pages, |meta| meta.weight);
sort_pages_by_weight(data)
}
@ -329,6 +323,10 @@ impl Library {
page.earlier = val2;
page.later = val1;
}
SortBy::UpdateDate => {
page.earlier_updated = val2;
page.later_updated = val1;
}
SortBy::Title => {
page.title_prev = val1;
page.title_next = val2;