rlua-searcher/tests/tests.rs
Andy Weidenbaum 801a04802a implement ClosureSearcher
expose `UserData` created from Rust

manipulate the `rlua::Context` in Rust and return string to load as chunk

primarily intended to enable constructing Lua modules in Rust
- particularly Lua modules involving `UserData`
2021-03-23 14:43:56 +11:00

237 lines
6.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use rlua::{Context, Lua, Table, UserData, UserDataMethods, Value};
use rlua_searcher::{AddSearcher, Result};
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
#[test]
fn add_searcher_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<String>>(|lua_ctx| {
lua_ctx.add_searcher(map)?;
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
}
#[test]
fn add_static_searcher_works() {
let lume = read_lume_to_str();
let name = "lume";
let mut map = HashMap::new();
map.insert(name, lume);
let lua = Lua::new();
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
lua_ctx.add_static_searcher(map)?;
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
}
#[test]
fn add_path_searcher_works() {
let name = "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 = HashMap::new();
map.insert(name, path);
let lua = Lua::new();
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
lua_ctx.add_path_searcher(map)?;
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
}
#[test]
fn module_reloading_works() {
let name = "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 = HashMap::new();
map.insert(name.clone(), path.clone());
let lua = Lua::new();
// Add searcher for lume module on disk, and read from it.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
lua_ctx.add_path_searcher(map)?;
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
// Twice.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
// Modify lume module on disk.
let mut out = File::create(path.clone()).expect("Could not create Lume module on disk");
write!(out, "{}\n", r#"return "hello again lume""#)
.expect("Could not modify Lume module on disk");
// Thrice. Should still be unchanged due to caching.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
// Remove lume module from Luas `package.loaded` cache to facilitate reload.
lua.context::<_, rlua::Result<()>>(|lua_ctx| {
let globals = lua_ctx.globals();
let loaded: Table = globals.get::<_, Table>("package")?.get("loaded")?;
loaded.set(name.clone(), Value::Nil)
})
.unwrap();
// Re-read from lume module on disk.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello again lume", hello);
// Twice.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello again lume", hello);
// Revert changes to lume module on disk.
let mut out = File::create(path).expect("Could not create Lume module on disk");
write!(out, "{}\n", r#"return "hello lume""#).expect("Could not modify Lume module on disk");
// Clear cache again.
lua.context::<_, rlua::Result<()>>(|lua_ctx| {
let globals = lua_ctx.globals();
let loaded: Table = globals.get::<_, Table>("package")?.get("loaded")?;
loaded.set(name, Value::Nil)
})
.unwrap();
// Ensure changes have been successfully reverted.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
}
#[test]
fn add_closure_searcher_works() {
let lua = Lua::new();
let mut modules: HashMap<
&'static str,
Box<dyn Fn(Context) -> rlua::Result<&'static str> + Send>,
> = HashMap::new();
let instrument_loader = Box::new(|lua_ctx: Context| {
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("return instrument")
});
modules.insert("instrument", instrument_loader);
let sound = lua
.context::<_, Result<String>>(|lua_ctx| {
lua_ctx.add_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,
}
impl Instrument {
pub fn new(name: String, sound: String) -> Self {
Self { name, sound }
}
pub fn play(&self) -> String {
format!("The {} goes {}", self.name, self.sound)
}
}
impl UserData for Instrument {
fn add_methods<'lua, M>(methods: &mut M)
where
M: UserDataMethods<'lua, Self>,
{
methods.add_method("play", |_, instrument, ()| Ok(instrument.play()));
}
}
fn read_lume_to_string() -> String {
r#"return "hello lume""#.to_string()
}
fn read_lume_to_str() -> &'static str {
r#"return "hello lume""#
}