use std::cell::Cell;
use std::rc::Rc;
use luaur_rt::{
Error, Function, Lua, MetaMethod, Result, UserData, UserDataFields, UserDataMethods,
};
#[test]
fn test_scope_func() -> Result<()> {
let lua = Lua::new();
let rc = Rc::new(Cell::new(0));
lua.scope(|scope| {
let rc2 = rc.clone();
let f = scope.create_function(move |_, ()| {
rc2.set(42);
Ok(())
})?;
lua.globals().set("f", &f)?;
f.call::<()>(())?;
assert_eq!(Rc::strong_count(&rc), 2);
Ok(())
})?;
assert_eq!(rc.get(), 42);
assert_eq!(Rc::strong_count(&rc), 1);
match lua.globals().get::<Function>("f")?.call::<()>(()) {
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
Error::CallbackDestructed => {}
ref err => panic!("wrong error type {:?}", err),
},
r => panic!("improper return for destructed function: {:?}", r),
};
Ok(())
}
#[test]
fn test_scope_capture() -> Result<()> {
let lua = Lua::new();
let mut i = 0;
lua.scope(|scope| {
scope
.create_function_mut(|_, ()| {
i = 42;
Ok(())
})?
.call::<()>(())
})?;
assert_eq!(i, 42);
Ok(())
}
#[test]
fn test_scope_outer_lua_access() -> Result<()> {
let lua = Lua::new();
let table = lua.create_table();
lua.scope(|scope| {
scope
.create_function(|_, ()| table.set("a", "b"))?
.call::<()>(())
})?;
assert_eq!(table.get::<String>("a")?, "b");
Ok(())
}
#[test]
fn test_scope_capture_scope() -> Result<()> {
let lua = Lua::new();
let i = Cell::new(0);
lua.scope(|scope| {
let f = scope.create_function(|_, ()| {
scope.create_function(|_, n: u32| {
i.set(i.get() + n);
Ok(())
})
})?;
f.call::<Function>(())?.call::<()>(10)?;
Ok(())
})?;
assert_eq!(i.get(), 10);
Ok(())
}
#[test]
fn test_scope_userdata_fields() -> Result<()> {
struct MyUserData<'a>(&'a Cell<i64>);
impl UserData for MyUserData<'_> {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field("field", "hello");
fields.add_field_method_get("val", |_, data| Ok(data.0.get()));
fields.add_field_method_set("val", |_, data, val| {
data.0.set(val);
Ok(())
});
}
}
let lua = Lua::new();
let i = Cell::new(42);
let f: Function = lua
.load(
r#"
function(u)
assert(u.field == "hello")
assert(u.val == 42)
u.val = 44
end
"#,
)
.eval()?;
lua.scope(|scope| f.call::<()>(scope.create_userdata(MyUserData(&i))?))?;
assert_eq!(i.get(), 44);
Ok(())
}
#[test]
fn test_scope_userdata_methods() -> Result<()> {
struct MyUserData<'a>(&'a Cell<i64>);
impl UserData for MyUserData<'_> {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
methods.add_method("dec", |_, data, ()| {
data.0.set(data.0.get() - 1);
Ok(())
});
}
}
let lua = Lua::new();
let i = Cell::new(42);
let f: Function = lua
.load(
r#"
function(u)
u:inc()
u:inc()
u:inc()
u:dec()
end
"#,
)
.eval()?;
lua.scope(|scope| f.call::<()>(scope.create_userdata(MyUserData(&i))?))?;
assert_eq!(i.get(), 44);
Ok(())
}
#[test]
fn test_scope_userdata_ops() -> Result<()> {
struct MyUserData<'a>(&'a i64);
impl UserData for MyUserData<'_> {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Add, |lua, this, ()| {
let globals = lua.globals();
globals.set("i", globals.get::<i64>("i")? + this.0)?;
Ok(())
});
methods.add_meta_method(MetaMethod::Sub, |lua, this, ()| {
let globals = lua.globals();
globals.set("i", globals.get::<i64>("i")? + this.0)?;
Ok(())
});
}
}
let lua = Lua::new();
let dummy = 1;
let f = lua
.load(
r#"
i = 0
return function(u)
_ = u + u
_ = u - 1
_ = u + 1
end
"#,
)
.eval::<Function>()?;
lua.scope(|scope| f.call::<()>(scope.create_userdata(MyUserData(&dummy))?))?;
assert_eq!(lua.globals().get::<i64>("i")?, 3);
Ok(())
}
#[test]
fn test_scope_userdata_drop() -> Result<()> {
let lua = Lua::new();
struct MyUserData<'a>(&'a Cell<i64>, #[allow(unused)] Rc<()>);
impl UserData for MyUserData<'_> {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
}
}
let (i, rc) = (Cell::new(1), Rc::new(()));
lua.scope(|scope| {
let ud = scope.create_userdata(MyUserData(&i, rc.clone()))?;
lua.globals().set("ud", ud)?;
lua.load("ud:inc()").exec()?;
assert_eq!(Rc::strong_count(&rc), 2);
Ok(())
})?;
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(i.get(), 2);
match lua.load("ud:inc()").exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::UserDataDestructed => {}
err => panic!("expected UserDataDestructed, got {err:?}"),
},
r => panic!("improper return for destructed userdata: {r:?}"),
};
Ok(())
}