#[derive(Debug)]
pub struct TemplateFrontmatter {
pub description: Option<String>,
}
pub fn parse_frontmatter(content: &str) -> TemplateFrontmatter {
let body = match find_closing_delimiter(content) {
Some((opening_len, closing_offset)) => &content[opening_len..closing_offset],
None => return TemplateFrontmatter { description: None },
};
let mut description = None;
for line in body.lines() {
if let Some(value) = line.strip_prefix("description:") {
let trimmed = value.trim();
if !trimmed.is_empty() {
description = Some(trimmed.to_string());
}
}
}
TemplateFrontmatter { description }
}
pub fn strip_frontmatter(content: &str) -> &str {
let (_, closing_offset) = match find_closing_delimiter(content) {
Some(result) => result,
None => return content,
};
let after_delimiter = &content[closing_offset + 3..];
after_delimiter
.strip_prefix("\r\n")
.or_else(|| after_delimiter.strip_prefix('\n'))
.unwrap_or(after_delimiter)
}
fn find_closing_delimiter(content: &str) -> Option<(usize, usize)> {
let opening_len = if content.starts_with("---\n") {
4
} else if content.starts_with("---\r\n") {
5
} else {
return None;
};
let rest = &content[opening_len..];
let mut offset = 0;
for line in rest.lines() {
if line == "---" {
return Some((opening_len, opening_len + offset));
}
offset += line.len();
if rest[offset..].starts_with("\r\n") {
offset += 2;
} else if rest[offset..].starts_with('\n') {
offset += 1;
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_frontmatter_with_description() {
let content = "---\ndescription: Implement a new feature\n---\n# Body\n";
let fm = parse_frontmatter(content);
assert_eq!(fm.description.as_deref(), Some("Implement a new feature"));
}
#[test]
fn test_parse_frontmatter_without_frontmatter() {
let content = "# Just a template\nNo frontmatter here.\n";
let fm = parse_frontmatter(content);
assert!(fm.description.is_none());
}
#[test]
fn test_parse_frontmatter_empty() {
let content = "---\n---\nBody text\n";
let fm = parse_frontmatter(content);
assert!(fm.description.is_none());
}
#[test]
fn test_parse_frontmatter_missing_closing() {
let content = "---\ndescription: Orphaned\nNo closing delimiter\n";
let fm = parse_frontmatter(content);
assert!(fm.description.is_none());
}
#[test]
fn test_parse_frontmatter_no_description_key() {
let content = "---\nauthor: someone\ntags: misc\n---\n# Body\n";
let fm = parse_frontmatter(content);
assert!(fm.description.is_none());
}
#[test]
fn test_strip_frontmatter_with_frontmatter() {
let content = "---\ndescription: A feature\n---\n# Feature\nBody here.\n";
let body = strip_frontmatter(content);
assert_eq!(body, "# Feature\nBody here.\n");
}
#[test]
fn test_strip_frontmatter_without_frontmatter() {
let content = "# No frontmatter\nJust content.\n";
let body = strip_frontmatter(content);
assert_eq!(body, content);
}
#[test]
fn test_strip_frontmatter_empty_frontmatter() {
let content = "---\n---\nbody";
let body = strip_frontmatter(content);
assert_eq!(body, "body");
}
#[test]
fn test_prompt_replacement_after_strip() {
let content = "---\ndescription: Fix a bug\n---\n# Fix\n{{PROMPT}}\nMore instructions.\n";
let body = strip_frontmatter(content);
assert!(body.contains("{{PROMPT}}"));
let replaced = body.replace("{{PROMPT}}", "Fix the login timeout issue");
assert!(replaced.contains("Fix the login timeout issue"));
assert!(!replaced.contains("{{PROMPT}}"));
assert!(!replaced.contains("description: Fix a bug"));
}
#[test]
fn test_parse_frontmatter_crlf() {
let content = "---\r\ndescription: A feature\r\n---\r\n# Body\r\n";
let fm = parse_frontmatter(content);
assert_eq!(fm.description.as_deref(), Some("A feature"));
}
#[test]
fn test_strip_frontmatter_crlf() {
let content = "---\r\ndescription: A feature\r\n---\r\n# Feature\r\nBody here.\r\n";
let body = strip_frontmatter(content);
assert_eq!(body, "# Feature\r\nBody here.\r\n");
}
#[test]
fn test_strip_frontmatter_missing_closing() {
let content = "---\ndescription: Orphaned\nNo closing delimiter\n";
let body = strip_frontmatter(content);
assert_eq!(body, content);
}
}