use std::collections::BTreeMap;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
fn main() {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or_default());
let skill_dir = manifest_dir.join("assets").join("skill");
let cmd_dir = manifest_dir.join("assets").join("plugin-commands");
rerun_if_changed(&skill_dir);
rerun_if_changed(&cmd_dir);
let skill_files = collect_files(&skill_dir);
let cmd_files = collect_files(&cmd_dir);
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap_or_default());
let dest = out_dir.join("skill_assets.rs");
let generated = render_manifest(&skill_files, &cmd_files);
fs::write(&dest, generated).expect("write skill_assets.rs");
}
fn collect_files(root: &Path) -> BTreeMap<String, PathBuf> {
let mut out = BTreeMap::new();
walk(root, root, &mut out);
out
}
fn walk(root: &Path, dir: &Path, out: &mut BTreeMap<String, PathBuf>) {
let entries = match fs::read_dir(dir) {
Ok(e) => e,
Err(_) => return,
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
walk(root, &path, out);
} else if let Ok(rel) = path.strip_prefix(root) {
let key = rel.to_string_lossy().replace('\\', "/");
out.insert(key, path.clone());
}
}
}
fn render_manifest(skill: &BTreeMap<String, PathBuf>, cmds: &BTreeMap<String, PathBuf>) -> String {
let mut s = String::new();
s.push_str("// @generated by build.rs — do not edit.\n");
s.push_str("/// Skill tree files: (relative path, contents).\n");
s.push_str("pub static SKILL_FILES: &[(&str, &str)] = &[\n");
for (rel, path) in skill {
s.push_str(&format!(
" ({:?}, include_str!({:?})),\n",
rel,
path.display().to_string()
));
}
s.push_str("];\n\n");
s.push_str("/// Slash-command files: (file name, contents).\n");
s.push_str("pub static COMMAND_FILES: &[(&str, &str)] = &[\n");
for (rel, path) in cmds {
s.push_str(&format!(
" ({:?}, include_str!({:?})),\n",
rel,
path.display().to_string()
));
}
s.push_str("];\n");
s
}
fn rerun_if_changed(path: &Path) {
println!("cargo:rerun-if-changed={}", path.display());
}