From 38c15af4dc0dbd7bd1c7b69488d547b37bf8c8ad Mon Sep 17 00:00:00 2001 From: Andy Weidenbaum Date: Thu, 4 Feb 2021 12:16:07 +1100 Subject: [PATCH] make initial implementation credit - https://github.com/AlphaModder/include-lua --- README.md | 20 +++++++++------- src/error.rs | 21 +++++++++++++++++ src/lib.rs | 14 ++++++------ src/searcher.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ src/types.rs | 3 +++ tests/tests.rs | 26 +++++++++++++++++++++ 6 files changed, 130 insertions(+), 15 deletions(-) create mode 100644 src/error.rs create mode 100644 src/searcher.rs create mode 100644 src/types.rs create mode 100644 tests/tests.rs diff --git a/README.md b/README.md index 544a6cd..fa7b305 100644 --- a/README.md +++ b/README.md @@ -11,20 +11,24 @@ and `require` the module. ## Synopsis ```rust -use rlua_searcher::AddSearcher; +use rlua::Lua; +use rlua_searcher::{AddSearcher, Result}; +use std::collections::HashMap; fn main() { let lume = read_lume_to_string(); - let name = "lume"; - let map = HashMap::new(); + let name = "lume".to_string(); + let mut map = HashMap::new(); map.insert(name, lume); - let lua = Lua::new; + let lua = Lua::new(); - let hello = lua.context::<_, rlua::Result<_>>(|lua_ctx| { - lua_ctx.add_searcher(map)?; - Ok(lua_ctx.load(r#"require("lume")"#).eval()?) - }).unwrap(); + let hello = lua + .context::<_, Result>(|lua_ctx| { + lua_ctx.add_searcher(map)?; + Ok(lua_ctx.load(r#"return require("lume")"#).eval()?) + }) + .unwrap(); // prints "hello lume" println!("{}", hello); diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..3a0398a --- /dev/null +++ b/src/error.rs @@ -0,0 +1,21 @@ +#[derive(Debug)] +pub enum Error { + RLua(rlua::Error), +} + +impl From for Error { + fn from(error: rlua::Error) -> Self { + Error::RLua(error) + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let res = match self { + Error::RLua(e) => format!("rlua error:\n{:#?}", e), + }; + write!(f, "{}", res) + } +} + +impl std::error::Error for Error {} diff --git a/src/lib.rs b/src/lib.rs index 31e1bb2..449e6fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +mod searcher; +mod error; +mod types; + +pub use crate::error::Error; +pub use crate::searcher::AddSearcher; +pub use crate::types::Result; diff --git a/src/searcher.rs b/src/searcher.rs new file mode 100644 index 0000000..bb2ad4a --- /dev/null +++ b/src/searcher.rs @@ -0,0 +1,61 @@ +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()) + } +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..13cf1f2 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,3 @@ +use crate::error::Error; + +pub type Result = std::result::Result; diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 0000000..c143229 --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,26 @@ +use rlua::Lua; +use rlua_searcher::{AddSearcher, Result}; +use std::collections::HashMap; + +#[test] +fn it_works() { + let lume = read_lume_to_string(); + let name = "lume".to_string(); + let mut map = HashMap::new(); + map.insert(name, lume); + + let lua = Lua::new(); + + let hello = lua + .context::<_, Result>(|lua_ctx| { + lua_ctx.add_searcher(map)?; + Ok(lua_ctx.load(r#"return require("lume")"#).eval()?) + }) + .unwrap(); + + assert_eq!("hello lume", hello); +} + +fn read_lume_to_string() -> String { + r#"return "hello lume""#.to_string() +}