use crate::error::{Error, Result};
use crate::state::Lua;
use crate::table::Table;
use crate::traits::IntoLua;
use crate::value::Value;
const MODULE_CACHE_KEY: &str = "__luaur_rt_modules";
impl Lua {
fn module_cache(&self) -> Result<Table> {
if let Ok(t) = self.named_registry_value::<Table>(MODULE_CACHE_KEY) {
self.ensure_require_installed(&t)?;
return Ok(t);
}
let t = self.create_table();
self.set_named_registry_value(MODULE_CACHE_KEY, &t)?;
self.ensure_require_installed(&t)?;
Ok(t)
}
fn ensure_require_installed(&self, cache: &Table) -> Result<()> {
let globals = self.globals();
if globals.contains_key("require")? {
return Ok(());
}
let cache = cache.clone();
let require = self.create_function(move |_lua, name: String| {
let exact = cache.get::<Value>(name.as_str())?;
let resolved = match exact {
Value::Nil => cache.get::<Value>(name.to_ascii_lowercase())?,
v => v,
};
match resolved {
Value::Nil => Err(Error::runtime(format!(
"module '{name}' not found: module was not registered"
))),
v => Ok(v),
}
})?;
globals.set("require", require)?;
Ok(())
}
pub fn register_module(&self, name: &str, module: impl IntoLua) -> Result<()> {
if !name.starts_with('@') {
return Err(Error::runtime("module name must begin with '@'"));
}
let value = module.into_lua(self)?;
let cache = self.module_cache()?;
cache.set(name, value.clone())?;
cache.set(name.to_ascii_lowercase(), value)?;
Ok(())
}
pub fn unload_module(&self, name: &str) -> Result<()> {
let cache = self.module_cache()?;
cache.set(name, Value::Nil)?;
cache.set(name.to_ascii_lowercase(), Value::Nil)?;
Ok(())
}
}