use std::path::Path;
use crate::frontmatter;
pub const ARGUMENTS_MARKER: &str = "**ARGUMENTS:**";
pub fn read_body(path: &Path) -> std::io::Result<String> {
let source = std::fs::read_to_string(path)?;
Ok(frontmatter::parse(&source).map_or(source.clone(), |(_, body)| body.to_string()))
}
#[must_use]
pub fn invocation_text(body: &str, args: &str) -> String {
let body = body.trim_end();
let args = args.trim();
if args.is_empty() {
return body.to_string();
}
format!("{body}\n\n{ARGUMENTS_MARKER} {args}")
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn body_is_verbatim_with_the_arguments_block_appended() {
let out = invocation_text("# Commit\nStage, then commit.\n", "fix the typo");
assert_eq!(
out,
"# Commit\nStage, then commit.\n\n**ARGUMENTS:** fix the typo"
);
}
#[test]
fn no_arguments_appends_nothing() {
assert_eq!(invocation_text("# Commit\n", ""), "# Commit");
assert_eq!(invocation_text("# Commit\n", " "), "# Commit");
}
#[test]
fn template_tokens_in_the_body_are_left_alone() {
let out = invocation_text("Use $ARGUMENTS and ${SKILL_DIR}/x.sh", "hello");
assert!(out.contains("$ARGUMENTS and ${SKILL_DIR}/x.sh"), "{out}");
assert!(out.ends_with("**ARGUMENTS:** hello"), "{out}");
}
#[test]
fn read_body_strips_frontmatter() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("SKILL.md");
fs::write(&path, "---\nname: x\ndescription: d\n---\n# Body\ntext\n").unwrap();
assert_eq!(read_body(&path).unwrap(), "# Body\ntext\n");
}
#[test]
fn read_body_without_frontmatter_returns_the_whole_file() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("SKILL.md");
fs::write(&path, "# Just markdown\n").unwrap();
assert_eq!(read_body(&path).unwrap(), "# Just markdown\n");
}
}