use rlua::{Context, MetaMethod, RegistryKey, Table, UserData, UserDataMethods, Value}; use std::collections::HashMap; use crate::types::Result; /// Stores Lua modules indexed by module name, and provides an /// `rlua::MetaMethod` to enable `require`ing the stored modules by name /// in an `rlua::Context`. struct Searcher { /// A `HashMap` of Lua modules in `String` representation, indexed /// by module name. modules: HashMap, /// An `rlua::RegistryKey` whose value is the Lua environment within /// which the user made the request to instantiate a `Searcher` for /// `modules`. globals: RegistryKey, } impl Searcher { fn new(modules: HashMap, globals: RegistryKey) -> Self { Self { modules, globals } } } impl UserData for Searcher { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_meta_method(MetaMethod::Call, |lua_ctx, this, name: String| { match this.modules.get(&name) { Some(content) => Ok(Value::Function( lua_ctx .load(content) .set_name(&name)? .set_environment(lua_ctx.registry_value::(&this.globals)?)? .into_function()?, )), None => Ok(Value::Nil), } }); } } /// Extend `rlua::Context` to support `require`ing Lua modules by name. pub trait AddSearcher<'a> { /// Add a `HashMap` of Lua modules indexed by module name to Lua’s /// `package.searchers` table in an `rlua::Context`, with lookup /// functionality provided by the `rlua_searcher::Searcher` struct. fn add_searcher(&self, modules: HashMap) -> Result<()>; } impl<'a> AddSearcher<'a> for Context<'a> { fn add_searcher(&self, modules: HashMap) -> Result<()> { let globals = self.globals(); let searchers: Table = globals.get::<_, Table>("package")?.get("searchers")?; let registry_key = self.create_registry_value(globals)?; let searcher = Searcher::new(modules, registry_key); searchers .set(searchers.len()? + 1, searcher) .map_err(|e| e.into()) } }