pub fn macro_preamble(names: &[String]) -> String {
let mut stems: Vec<&str> = names.iter().filter_map(|name| macro_stem(name)).collect();
stems.sort_unstable();
stems.dedup();
let mut out = String::new();
for stem in stems {
out.push_str(&format!(
"{{% import \"macros/{stem}.html\" as {stem} %}}\n"
));
}
out
}
fn macro_stem(name: &str) -> Option<&str> {
let rest = name.strip_prefix("macros/")?;
let stem = rest.strip_suffix(".html")?;
if stem.is_empty() || stem.contains('/') {
return None;
}
Some(stem)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn imports_only_flat_macro_files_sorted_and_deduped() {
let names = vec![
"post.html".to_string(),
"macros/youtube.html".to_string(),
"macros/note.html".to_string(),
"macros/sub/nested.html".to_string(), "macros/youtube.html".to_string(), "base.html".to_string(),
];
assert_eq!(
macro_preamble(&names),
"{% import \"macros/note.html\" as note %}\n\
{% import \"macros/youtube.html\" as youtube %}\n"
);
}
#[test]
fn empty_when_no_macro_files() {
let names = vec!["post.html".to_string(), "base.html".to_string()];
assert_eq!(macro_preamble(&names), "");
}
}