diff --git a/src/searcher.rs b/src/searcher.rs index 74ebfa2..73d36e3 100644 --- a/src/searcher.rs +++ b/src/searcher.rs @@ -136,8 +136,8 @@ where } } -/// Like `StaticSearcher`, but with closures as `modules` values, to facilitate setting -/// up an `rlua::Context` with Rust code. +/// Like `Searcher`, but with closures as `modules` values, to facilitate setting up an +/// `rlua::Context` with Rust code. /// /// Enables exposing `UserData` types to an `rlua::Context`. pub struct ClosureSearcher { @@ -200,6 +200,56 @@ impl UserData for ClosureSearcher { } } +/// Like `ClosureSearcher`, but with `&'static str` keys in `modules`. +pub struct StaticClosureSearcher { + modules: HashMap< + &'static str, + Box< + dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result> + + Send, + >, + >, + + globals: RegistryKey, +} + +impl StaticClosureSearcher { + pub fn new( + modules: HashMap< + &'static str, + Box< + dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result> + + Send, + >, + >, + globals: RegistryKey, + ) -> Self { + Self { modules, globals } + } +} + +impl UserData for StaticClosureSearcher { + fn add_methods<'lua, M>(methods: &mut M) + where + M: UserDataMethods<'lua, Self>, + { + 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), + } + }, + ); + } +} + /// Extend `rlua::Context` to support `require`ing Lua modules by name. pub trait AddSearcher { /// Add a `HashMap` of Lua modules indexed by module name to Lua’s @@ -227,8 +277,7 @@ pub trait AddSearcher { where P: 'static + AsRef + Send; - /// Like `add_static_searcher`, but with user-provided closure for `rlua::Context` - /// setup. + /// Like `add_searcher`, but with user-provided closure for `rlua::Context` setup. fn add_closure_searcher( &self, modules: HashMap< @@ -239,6 +288,18 @@ pub trait AddSearcher { >, >, ) -> Result<()>; + + /// Like `add_closure_searcher`, but with `&'static str` keys in `modules`. + fn add_static_closure_searcher( + &self, + modules: HashMap< + &'static str, + Box< + dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result> + + Send, + >, + >, + ) -> Result<()>; } impl<'a> AddSearcher for Context<'a> { @@ -312,4 +373,23 @@ impl<'a> AddSearcher for Context<'a> { .set(searchers.len()? + 1, searcher) .map_err(|e| e.into()) } + + fn add_static_closure_searcher( + &self, + 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")?; + let registry_key = self.create_registry_value(globals)?; + let searcher = StaticClosureSearcher::new(modules, registry_key); + searchers + .set(searchers.len()? + 1, searcher) + .map_err(|e| e.into()) + } } diff --git a/tests/tests.rs b/tests/tests.rs index c2f352b..bd390b6 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -212,6 +212,58 @@ fn add_closure_searcher_works() { assert_eq!(sound, "The ukulele goes twang"); } +#[test] +fn add_static_closure_searcher_works() { + let lua = Lua::new(); + + let mut modules: HashMap< + &'static str, + Box< + dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result> + + Send, + >, + > = HashMap::new(); + + 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)) + })?; + let tbl = lua_ctx.create_table()?; + tbl.set("new", new)?; + globals.set("instrument", tbl)?; + Ok(lua_ctx + .load("return instrument") + .set_name(name)? + .set_environment(env)? + .into_function()?) + }); + + modules.insert("instrument", instrument_loader); + + let sound = lua + .context::<_, Result>(|lua_ctx| { + lua_ctx.add_static_closure_searcher(modules)?; + + // Ensure global variable `instrument` is unset. + let nil: String = lua_ctx.load("return type(instrument)").eval()?; + assert_eq!(nil, "nil"); + + Ok(lua_ctx + .load( + r#"local instrument = require("instrument") + local ukulele = instrument.new("ukulele", "twang") + return ukulele:play()"#, + ) + .eval()?) + }) + .unwrap(); + + assert_eq!(sound, "The ukulele goes twang"); +} + struct Instrument { name: String, sound: String,