use std::collections::BTreeMap;
use std::env;
use std::fs;
use std::path::Path;
fn main() {
codegen_embedded_plugins();
}
fn path_to_forward_slashes(path: &Path) -> String {
path.to_string_lossy().replace('\\', "/")
}
fn codegen_embedded_plugins() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("embedded_plugins.rs");
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let embedded_dir = Path::new(&manifest_dir).join("embedded-plugins");
println!("cargo:rerun-if-changed=embedded-plugins");
if !embedded_dir.exists() {
let code = r#"
#[derive(Debug)]
pub struct EmbeddedPlugin {
pub metadata: &'static str,
pub hooks: &'static [(&'static str, &'static str)],
pub lib: &'static [(&'static str, &'static str)],
}
pub fn get_embedded_plugin(_name: &str) -> Option<&'static EmbeddedPlugin> {
None
}
pub fn list_embedded_plugins() -> &'static [&'static str] {
&[]
}
"#;
fs::write(&dest_path, code).unwrap();
return;
}
let mut plugins: BTreeMap<String, PluginFiles> = BTreeMap::new();
for entry in fs::read_dir(&embedded_dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if !path.is_dir() {
continue;
}
let dir_name = path.file_name().unwrap().to_string_lossy().to_string();
if !dir_name.starts_with("vfox-") {
continue;
}
println!("cargo:rerun-if-changed={}", path.display());
let hooks_dir = path.join("hooks");
if hooks_dir.exists() {
println!("cargo:rerun-if-changed={}", hooks_dir.display());
for entry in fs::read_dir(&hooks_dir).unwrap().flatten() {
if entry.path().extension().is_some_and(|ext| ext == "lua") {
println!("cargo:rerun-if-changed={}", entry.path().display());
}
}
}
let lib_dir = path.join("lib");
if lib_dir.exists() {
println!("cargo:rerun-if-changed={}", lib_dir.display());
for entry in fs::read_dir(&lib_dir).unwrap().flatten() {
if entry.path().extension().is_some_and(|ext| ext == "lua") {
println!("cargo:rerun-if-changed={}", entry.path().display());
}
}
}
let metadata_file = path.join("metadata.lua");
if metadata_file.exists() {
println!("cargo:rerun-if-changed={}", metadata_file.display());
}
let plugin = collect_plugin_files(&path);
plugins.insert(dir_name, plugin);
}
let mut code = String::new();
code.push_str(
r#"
#[derive(Debug)]
pub struct EmbeddedPlugin {
pub metadata: &'static str,
pub hooks: &'static [(&'static str, &'static str)],
pub lib: &'static [(&'static str, &'static str)],
}
"#,
);
for (name, files) in &plugins {
let var_name = name.replace('-', "_").to_uppercase();
code.push_str(&format!(
"static {var_name}: EmbeddedPlugin = EmbeddedPlugin {{\n"
));
let metadata_path = embedded_dir.join(name).join("metadata.lua");
code.push_str(&format!(
" metadata: include_str!(\"{}\"),\n",
path_to_forward_slashes(&metadata_path)
));
code.push_str(" hooks: &[\n");
for hook in &files.hooks {
let hook_path = embedded_dir
.join(name)
.join("hooks")
.join(format!("{}.lua", hook));
code.push_str(&format!(
" (\"{}\", include_str!(\"{}\")),\n",
hook,
path_to_forward_slashes(&hook_path)
));
}
code.push_str(" ],\n");
code.push_str(" lib: &[\n");
for lib in &files.lib {
let lib_path = embedded_dir
.join(name)
.join("lib")
.join(format!("{}.lua", lib));
code.push_str(&format!(
" (\"{}\", include_str!(\"{}\")),\n",
lib,
path_to_forward_slashes(&lib_path)
));
}
code.push_str(" ],\n");
code.push_str("};\n\n");
}
code.push_str("pub fn get_embedded_plugin(name: &str) -> Option<&'static EmbeddedPlugin> {\n");
code.push_str(" match name {\n");
for name in plugins.keys() {
let var_name = name.replace('-', "_").to_uppercase();
let short_name = name.strip_prefix("vfox-").unwrap_or(name);
code.push_str(&format!(
" \"{}\" | \"{}\" => Some(&{}),\n",
name, short_name, var_name
));
}
code.push_str(" _ => None,\n");
code.push_str(" }\n");
code.push_str("}\n\n");
code.push_str("pub fn list_embedded_plugins() -> &'static [&'static str] {\n");
code.push_str(" &[\n");
for name in plugins.keys() {
code.push_str(&format!(" \"{}\",\n", name));
}
code.push_str(" ]\n");
code.push_str("}\n");
fs::write(&dest_path, code).unwrap();
}
struct PluginFiles {
hooks: Vec<String>,
lib: Vec<String>,
}
fn collect_plugin_files(plugin_dir: &Path) -> PluginFiles {
let mut hooks = Vec::new();
let mut lib = Vec::new();
let hooks_dir = plugin_dir.join("hooks");
if hooks_dir.exists() {
for entry in fs::read_dir(&hooks_dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.extension().is_some_and(|ext| ext == "lua") {
let name = path.file_stem().unwrap().to_string_lossy().to_string();
hooks.push(name);
}
}
}
hooks.sort();
let lib_dir = plugin_dir.join("lib");
if lib_dir.exists() {
for entry in fs::read_dir(&lib_dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.extension().is_some_and(|ext| ext == "lua") {
let name = path.file_stem().unwrap().to_string_lossy().to_string();
lib.push(name);
}
}
}
lib.sort();
PluginFiles { hooks, lib }
}