From f05e0966cb15325e9d85e9479fe9e35f0c73fa14 Mon Sep 17 00:00:00 2001 From: atweiden Date: Mon, 27 Jun 2022 13:38:18 +1000 Subject: [PATCH] add CatSearcher --- Cargo.toml | 4 ++++ src/searcher.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++---- src/types.rs | 5 +++- tests/tests.rs | 33 ++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 303b672..89ad0e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,7 @@ description = "Require Lua modules by name" [dependencies] rlua = "0.18" + +[dependencies.io-cat] +git = "https://git.sr.ht/~ioiojo/io-cat" +rev = "e08a8b9776f36a9e8e015d7c9802dbf758187fe2" diff --git a/src/searcher.rs b/src/searcher.rs index 42d6813..e17ed08 100644 --- a/src/searcher.rs +++ b/src/searcher.rs @@ -5,7 +5,7 @@ use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; -use crate::types::Result; +use crate::types::{CatMap, 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`. @@ -118,7 +118,7 @@ where /// `rlua::Context` with Rust code. /// /// Enables exposing `UserData` types to an `rlua::Context`. -pub struct ClosureSearcher { +struct ClosureSearcher { /// Closures must accept three parameters: /// /// 1. An `rlua::Context`, which the closure can do what it wants with. @@ -142,7 +142,7 @@ pub struct ClosureSearcher { } impl ClosureSearcher { - pub fn new( + fn new( modules: HashMap< Cow<'static, str>, Box< @@ -182,7 +182,7 @@ impl UserData for ClosureSearcher { /// up an `rlua::Context` with Rust code. /// /// Enables exposing `UserData` types to an `rlua::Context`. -pub struct FunctionSearcher { +struct FunctionSearcher { /// Functions must accept three parameters: /// /// 1. An `rlua::Context`, which the function body can do what it wants with. @@ -203,7 +203,7 @@ pub struct FunctionSearcher { } impl FunctionSearcher { - pub fn new( + fn new( modules: HashMap< Cow<'static, str>, for<'ctx> fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result>, @@ -236,6 +236,45 @@ impl UserData for FunctionSearcher { } } +/// Like `Searcher`, but with `CatMap` to facilitate indexing heterogenous strings and paths - +/// all presumed to resolve to Lua module content - by module names in `modules`. +struct CatSearcher { + modules: CatMap, + globals: RegistryKey, +} + +impl CatSearcher { + fn new(modules: CatMap, globals: RegistryKey) -> Self { + Self { modules, globals } + } +} + +impl UserData for CatSearcher { + fn add_methods<'lua, M>(methods: &mut M) + where + M: UserDataMethods<'lua, Self>, + { + methods.add_meta_method(MetaMethod::Call, |lua_ctx, this, name: String| { + let name = Cow::from(name); + match this.modules.get(&name) { + Some(content) => { + let content = content + .cat() + .map_err(|e| rlua::Error::RuntimeError(format!("io error: {}", e)))?; + Ok(Value::Function( + lua_ctx + .load(&content) + .set_name(name.as_ref())? + .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 { /// Add a `HashMap` of Lua modules indexed by module name to Lua's `package.searchers` @@ -279,6 +318,10 @@ pub trait AddSearcher { for<'ctx> fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result>, >, ) -> Result<()>; + + /// Like `add_searcher`, except `modules` can contain heterogenous strings and paths + /// indexed by module name. + fn add_cat_searcher(&self, modules: CatMap) -> Result<()>; } impl<'a> AddSearcher for Context<'a> { @@ -358,4 +401,14 @@ impl<'a> AddSearcher for Context<'a> { .set(searchers.len()? + 1, searcher) .map_err(|e| e.into()) } + + fn add_cat_searcher(&self, modules: CatMap) -> Result<()> { + let globals = self.globals(); + let searchers: Table = globals.get::<_, Table>("package")?.get("searchers")?; + let registry_key = self.create_registry_value(globals)?; + let searcher = CatSearcher::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 index 13cf1f2..d461d45 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,3 +1,6 @@ use crate::error::Error; +use std::borrow::Cow; +use std::result; -pub type Result = std::result::Result; +pub(crate) type CatMap = io_cat::CatMap>; +pub type Result = result::Result; diff --git a/tests/tests.rs b/tests/tests.rs index 3b37820..d4513b4 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,3 +1,4 @@ +use io_cat::CatMap; use rlua::{Context, Function, Lua, Table, UserData, UserDataMethods, Value}; use rlua_searcher::{AddSearcher, Result}; use std::borrow::Cow; @@ -330,3 +331,35 @@ fn cartridge_loader<'ctx>( .set_environment(env)? .into_function()?) } + +#[test] +fn add_cat_searcher_works() { + let name = Cow::from("lume".to_string()); + let path = PathBuf::new() + .join(std::env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("tests") + .join("data") + .join("lume.lua"); + let mut map: CatMap> = CatMap::new(); + map.insert(name, Box::new(path)); + map.insert(Cow::from("loon"), Box::new(r#"return "hello loon""#)); + + let lua = Lua::new(); + + let hello = lua + .context::<_, Result>(|lua_ctx| { + lua_ctx.add_cat_searcher(map)?; + Ok(lua_ctx.load(r#"return require("lume")"#).eval()?) + }) + .unwrap(); + + assert_eq!("hello lume", hello); + + let hello = lua + .context::<_, Result>(|lua_ctx| { + Ok(lua_ctx.load(r#"return require("loon")"#).eval()?) + }) + .unwrap(); + + assert_eq!("hello loon", hello); +}