use std::fs;
use std::io::Write;
use std::path::Path;
fn main() {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let blog_dir = Path::new(&manifest_dir).join("src/blog");
let out_dir = std::env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("blog_posts.rs");
let mut output = String::new();
let mut md_files: Vec<_> = fs::read_dir(&blog_dir)
.expect("Failed to read blog directory")
.filter_map(|entry| entry.ok())
.filter(|entry| {
entry
.path()
.extension()
.map(|ext| ext == "md")
.unwrap_or(false)
})
.collect();
md_files.sort_by_key(|entry| entry.path());
for entry in md_files {
let path = entry.path();
let filename = path.file_name().unwrap().to_str().unwrap();
let absolute_path = blog_dir.join(filename);
let content = fs::read_to_string(&path).expect("Failed to read markdown file");
if let Some(post) = parse_frontmatter(&content, filename) {
output.push_str(&format!(
r#"blog_post! {{
post: {},
date: "{}",
tags: "{}",
content: include_str!("{}")
}}
"#,
post.module_name,
post.date,
post.tags.join(", "),
absolute_path.display()
));
}
println!("cargo::rerun-if-changed={}", path.display());
}
let mut file = fs::File::create(&dest_path).expect("Failed to create output file");
file.write_all(output.as_bytes())
.expect("Failed to write output file");
}
struct BlogPost {
module_name: String,
date: String,
tags: Vec<String>,
}
fn parse_frontmatter(content: &str, filename: &str) -> Option<BlogPost> {
let frontmatter = if content.starts_with("<!--") {
let end_idx = content.find("-->")?;
&content[4..end_idx]
} else if content.starts_with("---") {
let rest = &content[3..];
let end_idx = rest.find("---")?;
&rest[..end_idx]
} else {
eprintln!("Warning: {} has no frontmatter, skipping", filename);
return None;
};
let mut date = None;
let mut tags = Vec::new();
let mut module_name = None;
for line in frontmatter.lines() {
let line = line.trim();
if line.is_empty() {
continue;
}
if let Some(value) = line.strip_prefix("date:") {
date = Some(value.trim().trim_matches('"').to_string());
} else if let Some(value) = line.strip_prefix("tags:") {
let value = value.trim();
if value.starts_with('[') && value.ends_with(']') {
let inner = &value[1..value.len() - 1];
tags = inner
.split(',')
.map(|t| t.trim().trim_matches('"').trim_matches('\'').to_string())
.filter(|t| !t.is_empty())
.collect();
}
} else if let Some(value) = line.strip_prefix("post:") {
module_name = Some(value.trim().trim_matches('"').to_string());
}
}
let module_name = module_name.unwrap_or_else(|| {
let stem = filename.strip_suffix(".md").unwrap_or(filename);
let name = if stem.len() > 10 && stem.chars().nth(8) == Some('_') {
&stem[9..]
} else {
stem
};
name.replace('-', "_")
});
let date = date.unwrap_or_else(|| {
if filename.len() >= 8 {
let year = &filename[0..2];
let month = &filename[3..5];
let day = &filename[6..8];
format!("20{}-{}-{}", year, month, day)
} else {
"1970-01-01".to_string()
}
});
Some(BlogPost {
module_name,
date,
tags,
})
}