use crate::content::Site;
use anyhow::Result;
use std::io::Write;
use std::path::Path;
pub fn generate_sitemap(site: &Site, output_dir: &Path, base_url: &str) -> Result<()> {
let sitemap_path = output_dir.join("sitemap.xml");
let mut file = std::fs::File::create(&sitemap_path)?;
write!(file, r#"<?xml version="1.0" encoding="UTF-8"?>"#)?;
write!(
file,
r#"<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">"#
)?;
for page in &site.pages {
if page.is_draft {
continue;
}
write!(file, "<url>")?;
write!(
file,
"<loc>{}{}</loc>",
xml_escape(base_url),
xml_escape(&page.url)
)?;
if let Some(date) = page.date {
write!(file, "<lastmod>{}</lastmod>", date.format("%Y-%m-%d"))?;
}
let priority = match page.url.as_str() {
"/" => "1.0",
url if url.starts_with("/blog/") => "0.8",
url if url.starts_with("/notes/") => "0.7",
url if url.starts_with("/tags/") || url.starts_with("/contexts/") => "0.5",
_ => "0.6",
};
write!(file, "<priority>{}</priority>", priority)?;
write!(file, "</url>")?;
}
for snippet in &site.snippets {
write!(file, "<url>")?;
write!(
file,
"<loc>{}{}</loc>",
xml_escape(base_url),
xml_escape(&snippet.url)
)?;
if let Some(date) = snippet.date {
write!(file, "<lastmod>{}</lastmod>", date.format("%Y-%m-%d"))?;
}
write!(file, "<priority>0.6</priority>")?;
write!(file, "</url>")?;
}
write!(file, "</urlset>")?;
tracing::info!("Generated sitemap: {:?}", sitemap_path);
Ok(())
}
fn xml_escape(s: &str) -> String {
s.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::collections::Collection;
use crate::content::Page;
use tempfile::TempDir;
#[test]
fn test_generate_sitemap() {
let temp = TempDir::new().unwrap();
let mut site = Site::new();
let mut page = Page::new(std::path::PathBuf::from("test.md"), Collection::Blog);
page.url = "/blog/test/".to_string();
page.title = "Test".to_string();
site.pages.push(page);
let result = generate_sitemap(&site, temp.path(), "https://example.com");
assert!(result.is_ok());
let content = std::fs::read_to_string(temp.path().join("sitemap.xml")).unwrap();
assert!(content.contains("https://example.com/blog/test/"));
}
}