use std::path::PathBuf;
pub fn emit_target() {
println!(
"cargo:rustc-env=TARGET={}",
std::env::var("TARGET").unwrap()
);
}
fn manifest_dir() -> PathBuf {
PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
}
fn out_dir() -> PathBuf {
PathBuf::from(std::env::var("OUT_DIR").unwrap())
}
pub fn copy_skill_md() {
copy_skill_md_from("SKILL.md");
}
pub fn copy_skill_md_from(path: &str) {
let src = manifest_dir().join(path);
let dst = out_dir().join("SKILL.md");
println!("cargo:rerun-if-changed={path}");
std::fs::copy(&src, &dst).unwrap_or_else(|e| {
panic!(
"failed to copy {} to {}: {}",
src.display(),
dst.display(),
e
)
});
}
pub fn render_skill_md(render: impl FnOnce(&str) -> String) {
render_skill_md_from("SKILL.md", render);
}
pub fn render_skill_md_from(path: &str, render: impl FnOnce(&str) -> String) {
let src = manifest_dir().join(path);
println!("cargo:rerun-if-changed={path}");
let content = std::fs::read_to_string(&src)
.unwrap_or_else(|e| panic!("failed to read {}: {}", src.display(), e));
let rendered = render(&content);
let dst = out_dir().join("SKILL.md");
std::fs::write(&dst, rendered)
.unwrap_or_else(|e| panic!("failed to write {}: {}", dst.display(), e));
}
pub fn render_skill_md_vars(extra_vars: &[(&str, &str)]) {
render_skill_md_vars_from("SKILL.md", extra_vars);
}
pub fn render_skill_md_vars_from(path: &str, extra_vars: &[(&str, &str)]) {
render_skill_md_from(path, |content| substitute(content, extra_vars));
}
fn substitute(content: &str, extra_vars: &[(&str, &str)]) -> String {
let mut result = content.to_string();
let auto_vars = [
("version", "CARGO_PKG_VERSION"),
("name", "CARGO_PKG_NAME"),
("description", "CARGO_PKG_DESCRIPTION"),
];
for (key, env_var) in auto_vars {
if let Ok(val) = std::env::var(env_var) {
result = result.replace(&format!("{{{key}}}"), &val);
}
}
for (key, val) in extra_vars {
result = result.replace(&format!("{{{key}}}"), val);
}
result
}
pub fn read_file(path: &str) -> String {
let full = manifest_dir().join(path);
println!("cargo:rerun-if-changed={path}");
std::fs::read_to_string(&full)
.unwrap_or_else(|e| panic!("failed to read {}: {}", full.display(), e))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_substitute_auto_vars() {
let input = "name: {name}, version: {version}";
let result = substitute(input, &[]);
assert!(result.contains("ionem"));
assert!(!result.contains("{name}"));
}
#[test]
fn test_substitute_custom_vars() {
let input = "author: {author}, tool: {tool}";
let result = substitute(input, &[("author", "Alice"), ("tool", "mytool")]);
assert_eq!(result, "author: Alice, tool: mytool");
}
#[test]
fn test_substitute_mixed() {
let input = "{name} by {author}";
let result = substitute(input, &[("author", "Bob")]);
assert!(result.contains("ionem"));
assert!(result.contains("Bob"));
assert!(!result.contains("{author}"));
}
#[test]
fn test_substitute_no_match_left_alone() {
let input = "hello {unknown} world";
let result = substitute(input, &[]);
assert_eq!(result, "hello {unknown} world");
}
}