From f8c6ea2b00d4e889a0654ca7c12de2254db574d6 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Sat, 15 May 2021 21:16:55 +0300 Subject: [PATCH] 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. --- components/front_matter/src/lib.rs | 2 + components/front_matter/src/page.rs | 12 +++++ components/library/src/content/page.rs | 4 ++ components/library/src/content/ser.rs | 12 +++++ components/library/src/library.rs | 68 +++++++++++++------------- 5 files changed, 63 insertions(+), 35 deletions(-) diff --git a/components/front_matter/src/lib.rs b/components/front_matter/src/lib.rs index 12dd4393..ddfcd2b6 100644 --- a/components/front_matter/src/lib.rs +++ b/components/front_matter/src/lib.rs @@ -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 diff --git a/components/front_matter/src/page.rs b/components/front_matter/src/page.rs index 16ac57c9..d331521b 100644 --- a/components/front_matter/src/page.rs +++ b/components/front_matter/src/page.rs @@ -20,6 +20,12 @@ pub struct PageFrontMatter { /// Updated date #[serde(default, deserialize_with = "from_toml_datetime")] pub updated: Option, + /// Chrono converted update datatime + #[serde(default, skip_deserializing)] + pub updated_datetime: Option, + /// 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, @@ -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, diff --git a/components/library/src/content/page.rs b/components/library/src/content/page.rs index b131e07f..9f5e2667 100644 --- a/components/library/src/content/page.rs +++ b/components/library/src/content/page.rs @@ -60,6 +60,10 @@ pub struct Page { /// When is found in the text, will take the content up to that part /// as summary pub summary: Option, + /// The earlier updated page, for pages sorted by updated date + pub earlier_updated: Option, + /// The later updated page, for pages sorted by updated date + pub later_updated: Option, /// The earlier page, for pages sorted by date pub earlier: Option, /// The later page, for pages sorted by date diff --git a/components/library/src/content/ser.rs b/components/library/src/content/ser.rs index cf951b01..8d4a077b 100644 --- a/components/library/src/content/ser.rs +++ b/components/library/src/content/ser.rs @@ -90,6 +90,8 @@ pub struct SerializingPage<'a> { lang: &'a str, lighter: Option>>, heavier: Option>>, + earlier_updated: Option>>, + later_updated: Option>>, earlier: Option>>, later: Option>>, title_prev: Option>>, @@ -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, diff --git a/components/library/src/library.rs b/components/library/src/library.rs index 32a708d1..0a76952b 100644 --- a/components/library/src/library.rs +++ b/components/library/src/library.rs @@ -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, + field: impl Fn(&'a PageFrontMatter) -> Option, + ) -> Vec<(&'a DefaultKey, Option, &'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") - } - }) - .collect(); + let data = get_data(section, &self.pages, |meta| meta.datetime); + + sort_pages_by_date(data) + } + 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;