From 759838e7f59b68a3b04f54cb7b9f48208c96779d Mon Sep 17 00:00:00 2001 From: Timothy DeHerrera Date: Sat, 31 Jan 2026 22:13:09 -0700 Subject: [PATCH] refactor(main): integrate SiteManifest for unified content discovery Replace separate discover_nav + discover_sections calls with single SiteManifest::discover() in run() pipeline: - Update generate_feed() to accept &SiteManifest - Update generate_sitemap_file() to accept &SiteManifest - Simplify process_pages() to return Result<()> - Remove redundant all_posts collection (now in manifest.posts) - Remove unused discover_nav and discover_sections imports All 49 tests pass, site builds identically. --- src/feed.rs | 11 +++++++--- src/main.rs | 58 +++++++++++++++++++------------------------------- src/sitemap.rs | 11 +++++----- 3 files changed, 35 insertions(+), 45 deletions(-) diff --git a/src/feed.rs b/src/feed.rs index 5e7b017..d65d390 100644 --- a/src/feed.rs +++ b/src/feed.rs @@ -1,11 +1,16 @@ //! Atom feed generation. use crate::config::SiteConfig; -use crate::content::Content; +use crate::content::SiteManifest; use std::path::Path; -/// Generate an Atom 1.0 feed from blog posts. -pub fn generate_atom_feed(posts: &[Content], config: &SiteConfig, content_root: &Path) -> String { +/// Generate an Atom 1.0 feed from blog posts in the manifest. +pub fn generate_atom_feed( + manifest: &SiteManifest, + config: &SiteConfig, + content_root: &Path, +) -> String { + let posts = &manifest.posts; let base_url = config.base_url.trim_end_matches('/'); // Use the most recent post date as feed updated time, or fallback diff --git a/src/main.rs b/src/main.rs index 293f99f..9adc6a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ mod render; mod sitemap; mod template_engine; -use crate::content::{discover_nav, discover_sections, Content, ContentKind, NavItem}; +use crate::content::{Content, ContentKind, NavItem}; use crate::error::{Error, Result}; use crate::template_engine::{ContentContext, TemplateEngine}; use std::fs; @@ -99,17 +99,14 @@ fn run(config_path: &Path) -> Result<()> { // Load Tera templates let engine = TemplateEngine::new(&template_dir)?; - // Discover navigation from filesystem - let nav = discover_nav(&content_dir)?; + // Discover all site content in a single pass + let manifest = content::SiteManifest::discover(&content_dir)?; // 0. Copy static assets copy_static_assets(&static_dir, &output_dir)?; - // 1. Discover and process all sections - let sections = discover_sections(&content_dir)?; - let mut all_posts = Vec::new(); // For feed generation - - for section in §ions { + // 1. Process all sections + for section in &manifest.sections { eprintln!("processing section: {}", section.name); // Collect and sort items in this section @@ -120,7 +117,6 @@ fn run(config_path: &Path) -> Result<()> { "blog" => { // Blog: sort by date, newest first items.sort_by(|a, b| b.frontmatter.date.cmp(&a.frontmatter.date)); - all_posts.extend(items.iter().cloned()); } "projects" => { // Projects: sort by weight @@ -148,7 +144,8 @@ fn run(config_path: &Path) -> Result<()> { eprintln!(" processing: {}", item.slug); let html_body = render::markdown_to_html(&item.body); let page_path = format!("/{}", item.output_path(&content_dir).display()); - let html = engine.render_content(item, &html_body, &page_path, &config, &nav)?; + let html = + engine.render_content(item, &html_body, &page_path, &config, &manifest.nav)?; write_output(&output_dir, &content_dir, item, html)?; } @@ -164,15 +161,15 @@ fn run(config_path: &Path) -> Result<()> { &item_contexts, &page_path, &config, - &nav, - )?; + &manifest.nav, + ); let out_path = output_dir.join(§ion.name).join("index.html"); fs::create_dir_all(out_path.parent().unwrap()).map_err(|e| Error::CreateDir { path: out_path.parent().unwrap().to_path_buf(), source: e, })?; - fs::write(&out_path, html).map_err(|e| Error::WriteFile { + fs::write(&out_path, html?).map_err(|e| Error::WriteFile { path: out_path.clone(), source: e, })?; @@ -180,24 +177,18 @@ fn run(config_path: &Path) -> Result<()> { } // 2. Generate Atom feed (blog posts only) - if !all_posts.is_empty() { - generate_feed(&output_dir, &all_posts, &config, &content_dir)?; + if !manifest.posts.is_empty() { + generate_feed(&output_dir, &manifest, &config, &content_dir)?; } - // 3. Process standalone pages (discovered dynamically) - let standalone_pages = process_pages(&content_dir, &output_dir, &config, &nav, &engine)?; + // 3. Process standalone pages + process_pages(&content_dir, &output_dir, &config, &manifest.nav, &engine)?; // 4. Generate homepage - generate_homepage(&content_dir, &output_dir, &config, &nav, &engine)?; + generate_homepage(&content_dir, &output_dir, &config, &manifest.nav, &engine)?; // 5. Generate sitemap - generate_sitemap_file( - &output_dir, - §ions, - &standalone_pages, - &config, - &content_dir, - )?; + generate_sitemap_file(&output_dir, &manifest, &config, &content_dir)?; eprintln!("done!"); Ok(()) @@ -206,14 +197,14 @@ fn run(config_path: &Path) -> Result<()> { /// Generate the Atom feed fn generate_feed( output_dir: &Path, - posts: &[Content], + manifest: &content::SiteManifest, config: &config::SiteConfig, content_dir: &Path, ) -> Result<()> { let out_path = output_dir.join("feed.xml"); eprintln!("generating: {}", out_path.display()); - let feed_xml = feed::generate_atom_feed(posts, config, content_dir); + let feed_xml = feed::generate_atom_feed(manifest, config, content_dir); fs::write(&out_path, feed_xml).map_err(|e| Error::WriteFile { path: out_path.clone(), @@ -227,15 +218,14 @@ fn generate_feed( /// Generate the XML sitemap fn generate_sitemap_file( output_dir: &Path, - sections: &[content::Section], - pages: &[Content], + manifest: &content::SiteManifest, config: &config::SiteConfig, content_dir: &Path, ) -> Result<()> { let out_path = output_dir.join("sitemap.xml"); eprintln!("generating: {}", out_path.display()); - let sitemap_xml = sitemap::generate_sitemap(sections, pages, config, content_dir); + let sitemap_xml = sitemap::generate_sitemap(manifest, config, content_dir); fs::write(&out_path, sitemap_xml).map_err(|e| Error::WriteFile { path: out_path.clone(), @@ -247,16 +237,13 @@ fn generate_sitemap_file( } /// Process standalone pages in content/ (top-level .md files excluding _index.md) -/// Returns the discovered pages for use by sitemap generation. fn process_pages( content_dir: &Path, output_dir: &Path, config: &config::SiteConfig, nav: &[NavItem], engine: &TemplateEngine, -) -> Result> { - let mut pages = Vec::new(); - +) -> Result<()> { // Dynamically discover top-level .md files (except _index.md) let entries = fs::read_dir(content_dir).map_err(|e| Error::ReadFile { path: content_dir.to_path_buf(), @@ -277,10 +264,9 @@ fn process_pages( let html = engine.render_page(&content, &html_body, &page_path, config, nav)?; write_output(output_dir, content_dir, &content, html)?; - pages.push(content); } } - Ok(pages) + Ok(()) } /// Generate the homepage from content/_index.md diff --git a/src/sitemap.rs b/src/sitemap.rs index 0bd7990..959b094 100644 --- a/src/sitemap.rs +++ b/src/sitemap.rs @@ -1,7 +1,7 @@ //! XML sitemap generation for SEO. use crate::config::SiteConfig; -use crate::content::{Content, Section}; +use crate::content::SiteManifest; use std::path::Path; /// A URL entry for the sitemap. @@ -12,7 +12,7 @@ pub struct SitemapEntry { pub lastmod: Option, } -/// Generate an XML sitemap from discovered content. +/// Generate an XML sitemap from the site manifest. /// /// Includes: /// - Homepage @@ -20,8 +20,7 @@ pub struct SitemapEntry { /// - Section items (posts, projects, etc.) /// - Standalone pages pub fn generate_sitemap( - sections: &[Section], - pages: &[Content], + manifest: &SiteManifest, config: &SiteConfig, content_root: &Path, ) -> String { @@ -35,7 +34,7 @@ pub fn generate_sitemap( }); // Sections and their items - for section in sections { + for section in &manifest.sections { // Section index entries.push(SitemapEntry { loc: format!("{}/{}/index.html", base_url, section.name), @@ -55,7 +54,7 @@ pub fn generate_sitemap( } // Standalone pages - for page in pages { + for page in &manifest.pages { let relative_path = page.output_path(content_root); entries.push(SitemapEntry { loc: format!("{}/{}", base_url, relative_path.display()),