use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
use crate::sandbox::{LuaError, LuaRuntime};
#[derive(Debug, Clone)]
pub struct LuaExtension {
pub name: String,
pub path: PathBuf,
}
pub fn discover_extensions(
user_config_dir: &Path,
project_dir: Option<&Path>,
) -> Vec<LuaExtension> {
let mut extensions = Vec::new();
let mut dirs = vec![user_config_dir.join("lua")];
if let Some(project) = project_dir {
dirs.push(project.join(".imp").join("lua"));
}
let mut seen_names = BTreeSet::new();
for dir in &dirs {
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().is_some_and(|e| e == "lua") {
let name = path
.file_stem()
.map(|s| s.to_string_lossy().to_string())
.unwrap_or_default();
if seen_names.insert(name.clone()) {
extensions.push(LuaExtension { name, path });
}
continue;
}
if path.is_dir() {
let init = path.join("init.lua");
if init.exists() {
let name = path
.file_name()
.map(|s| s.to_string_lossy().to_string())
.unwrap_or_default();
if seen_names.insert(name.clone()) {
extensions.push(LuaExtension { name, path: init });
}
}
}
}
}
}
extensions
}
pub fn load_extensions(
runtime: &LuaRuntime,
extensions: &[LuaExtension],
) -> Vec<(String, Result<(), LuaError>)> {
extensions
.iter()
.map(|ext| {
let result = runtime.exec_file(&ext.path);
(ext.name.clone(), result)
})
.collect()
}
pub fn reload(
user_config_dir: &Path,
project_dir: Option<&Path>,
policy: &imp_core::config::LuaCapabilityPolicy,
) -> Result<(LuaRuntime, Vec<LuaExtension>), LuaError> {
let extensions = discover_extensions(user_config_dir, project_dir);
let runtime = LuaRuntime::new()?;
crate::bridge::setup_host_api(&runtime)?;
runtime.apply_capability_policy(policy);
load_extensions(&runtime, &extensions);
Ok((runtime, extensions))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn discover_extensions_deduplicates_global_and_project_names() {
let temp = tempfile::tempdir().unwrap();
let user_config = temp.path().join("user");
let project = temp.path().join("project");
std::fs::create_dir_all(user_config.join("lua")).unwrap();
std::fs::create_dir_all(project.join(".imp").join("lua")).unwrap();
std::fs::write(user_config.join("lua").join("imp-update.lua"), "").unwrap();
std::fs::write(project.join(".imp").join("lua").join("imp-update.lua"), "").unwrap();
let extensions = discover_extensions(&user_config, Some(&project));
assert_eq!(extensions.len(), 1);
assert_eq!(extensions[0].name, "imp-update");
assert_eq!(
extensions[0].path,
user_config.join("lua").join("imp-update.lua")
);
}
}