From 9d2af25c8104e3845414a75417b177b2de4d7fd9 Mon Sep 17 00:00:00 2001 From: gered Date: Wed, 28 Jun 2023 16:59:10 -0400 Subject: [PATCH] improve how posts and pages are referenced internally since most often, we need to get posts sorted in descending order by date, we now just store the actual posts in a Vec, pre-sorted by date. everything else that needs to reference posts now does so by the post's index in that Vec. pages are also changed to be stored/referenced in a similar manner, but it is somewhat less important for pages since we don't do any real enumeration of pages at all anywhere. but it's nice to be consistent. --- src/site.rs | 88 ++++++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/src/site.rs b/src/site.rs index 646343c..0f8ba47 100644 --- a/src/site.rs +++ b/src/site.rs @@ -71,32 +71,31 @@ impl OldUrlMappings { } } -pub struct UrlsByTag { - mapping: HashMap>, +pub struct PostsByTag { + mapping: HashMap>, } -impl UrlsByTag { +impl PostsByTag { pub fn new() -> Self { - UrlsByTag { mapping: HashMap::new() } + PostsByTag { mapping: HashMap::new() } } #[inline] - pub fn get(&self, tag: &Tag) -> Option<&[UriPath]> { + pub fn get(&self, tag: &Tag) -> Option<&[usize]> { self.mapping.get(tag).map(|x| x.as_slice()) } - pub fn add_mapping(&mut self, url: &UriPath, tag: &Tag) { - if let Some(urls) = self.mapping.get_mut(tag) { - urls.push(url.clone()); + pub fn add_mapping(&mut self, post_index: usize, tag: &Tag) { + if let Some(indices) = self.mapping.get_mut(tag) { + indices.push(post_index); } else { - let urls = vec![url.clone()]; - self.mapping.insert(tag.clone(), urls); + self.mapping.insert(tag.clone(), vec![post_index]); } } - pub fn add_mappings(&mut self, url: &UriPath, tags: &[Tag]) { + pub fn add_mappings(&mut self, post_index: usize, tags: &[Tag]) { for tag in tags.iter() { - self.add_mapping(url, tag); + self.add_mapping(post_index, tag); } } } @@ -173,84 +172,91 @@ pub enum Content<'a> { } pub struct SiteContent { - pub pages: HashMap, - pub posts: HashMap, + pub pages: Vec, + pub posts: Vec, + pub pages_by_url: HashMap, + pub posts_by_url: HashMap, pub old_url_mappings: OldUrlMappings, - pub post_tag_mappings: UrlsByTag, - pub sorted_post_urls: Vec, + pub post_tag_mappings: PostsByTag, pub rss: RssMetadata, } impl SiteContent { pub fn new(pages_config: config::Pages, posts_config: config::Posts) -> Result { let mut old_url_mappings = OldUrlMappings::new(); - let mut post_tag_mappings = UrlsByTag::new(); - let mut sorted_post_urls = Vec::::new(); + let mut post_tag_mappings = PostsByTag::new(); // load pages - let mut pages = HashMap::::new(); - for page_config in pages_config.pages.iter() { + let mut pages = Vec::new(); + let mut pages_by_url = HashMap::new(); + for (index, page_config) in pages_config.pages.iter().enumerate() { let page = Page::try_from(page_config.clone())?; if let Some(old_urls) = &page_config.old_urls { old_url_mappings.add_mappings(old_urls, &page.url); } - pages.insert(page.url.clone(), page); + pages_by_url.insert(page.url.clone(), index); + pages.push(page); } - // load posts - let mut posts = HashMap::::new(); - for post_config in posts_config.posts.iter() { + // load posts, iterating over the config's list of posts in descending order by date so that + // our final post list is pre-sorted this way, as well as the post lists per tag + let mut posts = Vec::new(); + let mut posts_by_url = HashMap::new(); + for (index, post_config) in posts_config.posts.iter().sorted_by(|a, b| b.date.cmp(&a.date)).enumerate() { let post = Post::try_from(post_config.clone())?; if let Some(old_urls) = &post_config.old_urls { old_url_mappings.add_mappings(old_urls, &post.url); } - posts.insert(post.url.clone(), post); - } - - // build pre-sorted post urls table. as well, build the post url by tag mapping here so that - // the post urls for each tag will already be ordered by date - for post in posts.values().sorted_by(|a, b| b.date.cmp(&a.date)) { - sorted_post_urls.push(post.url.clone()); - post_tag_mappings.add_mappings(&post.url, &post.tags); + posts_by_url.insert(post.url.clone(), index); + post_tag_mappings.add_mappings(index, &post.tags); + posts.push(post); } let rss = RssMetadata::from(posts_config.rss); - Ok(SiteContent { pages, posts, old_url_mappings, post_tag_mappings, sorted_post_urls, rss }) + Ok(SiteContent { pages, posts, pages_by_url, posts_by_url, old_url_mappings, post_tag_mappings, rss }) + } + + pub fn get_page_by_url(&self, url: &UriPath) -> Option<&Page> { + self.pages_by_url.get(url).map(|index| self.pages.get(*index).unwrap()) + } + + pub fn get_post_by_url(&self, url: &UriPath) -> Option<&Post> { + self.posts_by_url.get(url).map(|index| self.posts.get(*index).unwrap()) } pub fn get_content_at(&self, url: &UriPath) -> Option { if let Some(new_url) = self.old_url_mappings.get(url) { Some(Content::Redirect(new_url.clone())) - } else if let Some(post) = self.posts.get(url) { + } else if let Some(post) = self.get_post_by_url(url) { Some(Content::Post(post)) - } else if let Some(page) = self.pages.get(url) { + } else if let Some(page) = self.get_page_by_url(url) { Some(Content::Page(page)) } else { None } } - pub fn get_posts_ordered_by_date(&self) -> Vec<&Post> { - self.sorted_post_urls.iter().map(|post_url| self.posts.get(post_url).unwrap()).collect() + pub fn get_posts_ordered_by_date(&self) -> &[Post] { + self.posts.as_slice() } pub fn get_posts_with_tag_ordered_by_date(&self, tag: &Tag) -> Vec<&Post> { let mut posts = Vec::new(); - if let Some(post_urls) = self.post_tag_mappings.get(tag) { - for url in post_urls.iter() { - posts.push(self.posts.get(url).unwrap()) + if let Some(post_indices) = self.post_tag_mappings.get(tag) { + for post_index in post_indices.iter() { + posts.push(self.posts.get(*post_index).unwrap()) } } posts } pub fn get_latest_post(&self) -> Option<&Post> { - self.sorted_post_urls.first().map(|post_url| self.posts.get(post_url).unwrap()) + self.posts.first() } }