diff --git a/src/searcher.rs b/src/searcher.rs index bf2d8b2..ccda749 100644 --- a/src/searcher.rs +++ b/src/searcher.rs @@ -1,5 +1,5 @@ use rlua::prelude::LuaError; -use rlua::{Context, MetaMethod, RegistryKey, Table, UserData, UserDataMethods, Value}; +use rlua::{Context, Function, MetaMethod, RegistryKey, Table, UserData, UserDataMethods, Value}; use std::collections::HashMap; use std::fs::File; use std::io::Read; @@ -132,20 +132,38 @@ where /// up an `rlua::Context` with Rust code. /// /// Enables exposing `UserData` types to an `rlua::Context`. -/// -/// Closures must return an `rlua::Result`-wrapped `&'static str`. This string is -/// subsequently passed into `rlua::Context.load()` and evaluated. pub struct ClosureSearcher { - /// Closures must accept an `rlua::Context` as their only parameter, and can do with - /// it what they wish. Closures must return an `rlua::Result`-wrapped `&'static str`. - modules: HashMap<&'static str, Box rlua::Result<&'static str> + Send>>, + /// Closures must accept three parameters: + /// + /// 1. An `rlua::Context`, which the closure can do what it wants with. + /// + /// 2. An `rlua::Table` containing globals (i.e. Lua’s `_G`), which can be passed + /// to `Chunk.set_environment()`. + /// + /// 3. The name of the module to be loaded (`&str`). + /// + /// Closures must return an `rlua::Result`-wrapped `Function`. This `Function` + /// acts as the module loader. + modules: HashMap< + &'static str, + Box< + dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result> + + Send, + >, + >, globals: RegistryKey, } impl ClosureSearcher { pub fn new( - modules: HashMap<&'static str, Box rlua::Result<&'static str> + Send>>, + modules: HashMap< + &'static str, + Box< + dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result> + + Send, + >, + >, globals: RegistryKey, ) -> Self { Self { modules, globals } @@ -157,22 +175,20 @@ impl UserData for ClosureSearcher { where M: UserDataMethods<'lua, Self>, { - methods.add_meta_method(MetaMethod::Call, |lua_ctx, this, name: String| { - let name = name.as_str(); - match this.modules.get(name) { - Some(ref loader) => { - let content = loader(lua_ctx)?; - Ok(Value::Function( - lua_ctx - .load(content) - .set_name(name)? - .set_environment(lua_ctx.registry_value::(&this.globals)?)? - .into_function()?, - )) + methods.add_meta_method( + MetaMethod::Call, + |lua_ctx: Context<'lua>, this, name: String| { + let name = name.as_str(); + match this.modules.get(name) { + Some(ref closure) => Ok(Value::Function(closure( + lua_ctx, + lua_ctx.registry_value::
(&this.globals)?, + name, + )?)), + None => Ok(Value::Nil), } - None => Ok(Value::Nil), - } - }); + }, + ); } } @@ -207,7 +223,13 @@ pub trait AddSearcher { /// setup. fn add_closure_searcher( &self, - modules: HashMap<&'static str, Box rlua::Result<&'static str> + Send>>, + modules: HashMap< + &'static str, + Box< + dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result> + + Send, + >, + >, ) -> Result<()>; } @@ -266,7 +288,13 @@ impl<'a> AddSearcher for Context<'a> { fn add_closure_searcher( &self, - modules: HashMap<&'static str, Box rlua::Result<&'static str> + Send>>, + modules: HashMap< + &'static str, + Box< + dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result> + + Send, + >, + >, ) -> Result<()> { let globals = self.globals(); let searchers: Table = globals.get::<_, Table>("package")?.get("searchers")?; diff --git a/tests/tests.rs b/tests/tests.rs index 51e9bff..2d3457d 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,4 +1,4 @@ -use rlua::{Context, Lua, Table, UserData, UserDataMethods, Value}; +use rlua::{Context, Function, Lua, Table, UserData, UserDataMethods, Value}; use rlua_searcher::{AddSearcher, Result}; use std::collections::HashMap; use std::fs::File; @@ -166,10 +166,15 @@ fn add_closure_searcher_works() { let mut modules: HashMap< &'static str, - Box rlua::Result<&'static str> + Send>, + Box< + dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result> + + Send, + >, > = HashMap::new(); - let instrument_loader = Box::new(|lua_ctx: Context| { + let instrument_loader: Box< + dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result> + Send, + > = Box::new(|lua_ctx, env, name| { let globals = lua_ctx.globals(); let new = lua_ctx.create_function(|_, (name, sound): (String, String)| { Ok(Instrument::new(name, sound)) @@ -177,7 +182,11 @@ fn add_closure_searcher_works() { let tbl = lua_ctx.create_table()?; tbl.set("new", new)?; globals.set("instrument", tbl)?; - Ok("return instrument") + Ok(lua_ctx + .load("return instrument") + .set_name(name)? + .set_environment(env)? + .into_function()?) }); modules.insert("instrument", instrument_loader);