use std::path::PathBuf;
use include_dir::{include_dir, Dir};
pub static DEFAULT_THEME: Dir<'_> =
include_dir!("$CARGO_MANIFEST_DIR/src/infra/driven/site/default_theme");
pub fn enumerate_templates() -> Vec<(String, String)> {
let mut out = Vec::new();
collect_html(&DEFAULT_THEME, &mut out);
out
}
fn collect_html(dir: &Dir<'_>, out: &mut Vec<(String, String)>) {
for file in dir.files() {
let path = file.path();
let is_html = path.extension().is_some_and(|e| e == "html");
let is_in_assets = path.starts_with("assets");
if is_html && !is_in_assets {
if let (Some(name), Some(body)) = (path.to_str(), file.contents_utf8()) {
out.push((name.to_string(), body.to_string()));
}
}
}
for sub in dir.dirs() {
collect_html(sub, out);
}
}
pub fn enumerate_assets() -> Vec<(PathBuf, &'static [u8])> {
let mut out = Vec::new();
if let Some(assets) = DEFAULT_THEME.get_dir("assets") {
collect_assets(assets, &mut out);
}
out
}
fn collect_assets(dir: &Dir<'static>, out: &mut Vec<(PathBuf, &'static [u8])>) {
for file in dir.files() {
out.push((file.path().to_path_buf(), file.contents()));
}
for sub in dir.dirs() {
collect_assets(sub, out);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn enumerates_at_least_the_homepage_layout() {
let templates = enumerate_templates();
let names: Vec<&str> = templates.iter().map(|(n, _)| n.as_str()).collect();
assert!(names.contains(&"layouts/index.html"), "got: {names:?}");
assert!(
names.contains(&"layouts/_default/single.html"),
"got: {names:?}"
);
assert!(
names.contains(&"layouts/_default/list.html"),
"got: {names:?}"
);
assert!(names.contains(&"layouts/base.html"), "got: {names:?}");
assert!(names.contains(&"layouts/404.html"), "got: {names:?}");
}
#[test]
fn enumerates_the_canonical_shortcodes() {
let templates = enumerate_templates();
let names: Vec<&str> = templates.iter().map(|(n, _)| n.as_str()).collect();
for sc in [
"shortcodes/adr-ref.html",
"shortcodes/ddr-ref.html",
"shortcodes/issue-ref.html",
"shortcodes/callout.html",
"shortcodes/toc.html",
] {
assert!(names.contains(&sc), "missing {sc} in {names:?}");
}
}
#[test]
fn enumerates_assets_does_not_include_layouts() {
let assets = enumerate_assets();
let names: Vec<String> = assets
.iter()
.map(|(p, _)| p.to_string_lossy().to_string())
.collect();
assert!(
names.iter().any(|n| n == "assets/style.css"),
"got: {names:?}"
);
assert!(
!names.iter().any(|n| n.contains("layouts")),
"assets enumerator leaked layouts: {names:?}"
);
}
#[test]
fn template_bodies_are_non_empty() {
for (name, body) in enumerate_templates() {
assert!(!body.trim().is_empty(), "{name} body is empty");
}
}
}