pub fn split_frontmatter(text: &str) -> (String, String) {
let lines: Vec<&str> = text.lines().collect();
let mut start_idx = 0;
while start_idx < lines.len() && lines[start_idx].trim().is_empty() {
start_idx += 1;
}
if start_idx >= lines.len() || lines[start_idx].trim() != "---" {
return (String::new(), text.to_string());
}
let mut end_idx = start_idx + 1;
while end_idx < lines.len() {
if lines[end_idx].trim() == "---" {
let frontmatter = lines[start_idx..=end_idx].join("\n") + "\n";
let content = lines[end_idx + 1..].join("\n");
return (frontmatter, content);
}
end_idx += 1;
}
(text.to_string(), String::new())
}
pub fn has_frontmatter(text: &str) -> bool {
let first_content_line = text.lines().find(|l| !l.trim().is_empty());
if first_content_line != Some("---") {
return false;
}
let after_opening = text.lines().skip_while(|l| l.trim().is_empty()).skip(1); after_opening.into_iter().any(|l| l.trim() == "---")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_split_frontmatter_present() {
let text = "---\ntitle: Test\n---\n# Hello\n";
let (fm, content) = split_frontmatter(text);
assert_eq!(fm, "---\ntitle: Test\n---\n");
assert_eq!(content, "# Hello");
}
#[test]
fn test_split_frontmatter_absent() {
let text = "# Hello\nWorld\n";
let (fm, content) = split_frontmatter(text);
assert_eq!(fm, "");
assert_eq!(content, "# Hello\nWorld\n");
}
#[test]
fn test_has_frontmatter() {
assert!(has_frontmatter("---\ntitle: Test\n---\nContent"));
assert!(!has_frontmatter("# No frontmatter"));
}
#[test]
fn test_no_closing_delimiter() {
let text = "---\ntitle: Test\nno closing";
let (fm, content) = split_frontmatter(text);
assert_eq!(fm, text);
assert_eq!(content, "");
}
}