use crate::config::FrontMatter;
pub fn parse_front_matter(source: &str) -> (FrontMatter, String) {
let source = source.strip_prefix('\u{FEFF}').unwrap_or(source);
let after_open = if source.starts_with("---\r\n") {
&source[5..]
} else if source.starts_with("---\n") {
&source[4..]
} else {
return (FrontMatter::default(), source.to_string());
};
const END_MARKER: &str = "\n---";
if let Some(idx) = after_open.find(END_MARKER) {
let fm_str = &after_open[..idx];
let after_marker = &after_open[idx + END_MARKER.len()..];
let body = if after_marker.starts_with("\r\n") {
after_marker[2..].to_string()
} else if after_marker.starts_with('\n') {
after_marker[1..].to_string()
} else {
after_marker.to_string()
};
let fm = serde_yaml::from_str(fm_str).unwrap_or_default();
(fm, body)
} else {
(FrontMatter::default(), source.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_front_matter_unix() {
let src = "---\ntitle: My Doc\nslug: my-doc\n---\n# Body text";
let (fm, body) = parse_front_matter(src);
assert_eq!(fm.title.as_deref(), Some("My Doc"));
assert_eq!(fm.slug.as_deref(), Some("my-doc"));
assert_eq!(body, "# Body text");
}
#[test]
fn parse_front_matter_windows() {
let src = "---\r\ntitle: Win\r\n---\r\n# Body";
let (fm, body) = parse_front_matter(src);
assert_eq!(fm.title.as_deref(), Some("Win"));
assert_eq!(body, "# Body");
}
#[test]
fn parse_front_matter_windows_lekton_import() {
let src = "---\r\nlekton-import: true\r\ntitle: Win\r\n---\r\n# Body";
let (fm, _) = parse_front_matter(src);
assert!(
fm.lekton_import,
"lekton_import should be true with CRLF line endings"
);
}
#[test]
fn parse_front_matter_utf8_bom() {
let src = "\u{FEFF}---\nlekton-import: true\ntitle: BOM Doc\n---\n# Body";
let (fm, body) = parse_front_matter(src);
assert!(
fm.lekton_import,
"lekton_import should be true despite leading BOM"
);
assert_eq!(fm.title.as_deref(), Some("BOM Doc"));
assert_eq!(body, "# Body");
}
#[test]
fn parse_front_matter_utf8_bom_crlf() {
let src = "\u{FEFF}---\r\nlekton-import: true\r\ntitle: BOM CRLF\r\n---\r\n# Body";
let (fm, _) = parse_front_matter(src);
assert!(
fm.lekton_import,
"lekton_import should be true with BOM + CRLF"
);
}
#[test]
fn parse_front_matter_missing() {
let src = "# No front matter\n\nJust a doc.";
let (fm, body) = parse_front_matter(src);
assert!(fm.title.is_none());
assert_eq!(body, src);
}
#[test]
fn parse_front_matter_accepts_kebab_case_keys() {
let src = "---\naccess-level: public\nservice-owner: docs\nparent-slug: guides\nis-hidden: true\nlekton-import: true\n---\n# Body";
let (fm, _) = parse_front_matter(src);
assert_eq!(fm.access_level.as_deref(), Some("public"));
assert_eq!(fm.service_owner.as_deref(), Some("docs"));
assert_eq!(fm.parent_slug.as_deref(), Some("guides"));
assert_eq!(fm.is_hidden, Some(true));
assert!(fm.lekton_import);
}
#[test]
fn parse_front_matter_accepts_camel_case_keys() {
let src = "---\nsummary: Brief guide to the platform deployment workflow.\naccessLevel: developer\nserviceOwner: platform\nparentSlug: docs\nisHidden: true\nlektonImport: true\n---\n# Body";
let (fm, _) = parse_front_matter(src);
assert_eq!(
fm.summary.as_deref(),
Some("Brief guide to the platform deployment workflow.")
);
assert_eq!(fm.access_level.as_deref(), Some("developer"));
assert_eq!(fm.service_owner.as_deref(), Some("platform"));
assert_eq!(fm.parent_slug.as_deref(), Some("docs"));
assert_eq!(fm.is_hidden, Some(true));
assert!(fm.lekton_import);
}
}