use std::cell::RefCell;
use std::marker::PhantomData;
use std::mem;
use crate::callback::{create_callback_function, destruct_callback, BoxedCallback};
use crate::error::{Error, Result};
use crate::function::Function;
use crate::multi::MultiValue;
use crate::state::Lua;
use crate::traits::{FromLuaMulti, IntoLuaMulti};
use crate::userdata::{create_scoped_userdata, AnyUserData, UserData};
pub struct Scope<'scope, 'env: 'scope> {
lua: Lua,
destructors: Destructors,
_scope_invariant: PhantomData<&'scope mut &'scope ()>,
_env_invariant: PhantomData<&'env mut &'env ()>,
}
struct Destructors {
list: RefCell<Vec<Box<dyn FnOnce()>>>,
}
impl Drop for Destructors {
fn drop(&mut self) {
let mut list = mem::take(&mut *self.list.borrow_mut());
while let Some(destructor) = list.pop() {
destructor();
}
}
}
impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
pub(crate) fn new(lua: Lua) -> Self {
Scope {
lua,
destructors: Destructors {
list: RefCell::new(Vec::new()),
},
_scope_invariant: PhantomData,
_env_invariant: PhantomData,
}
}
pub fn create_function<F, A, R>(&'scope self, func: F) -> Result<Function>
where
F: Fn(&Lua, A) -> Result<R> + 'scope,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let boxed: Box<dyn Fn(&Lua, MultiValue) -> Result<MultiValue> + 'scope> =
Box::new(move |lua, args| {
let a = A::from_lua_multi(args, lua)?;
let r = func(lua, a)?;
r.into_lua_multi(lua)
});
let boxed: BoxedCallback = unsafe {
mem::transmute::<
Box<dyn Fn(&Lua, MultiValue) -> Result<MultiValue> + 'scope>,
BoxedCallback,
>(boxed)
};
let f = create_callback_function(&self.lua, boxed)?;
let f_for_dtor = f.clone();
self.destructors
.list
.borrow_mut()
.push(Box::new(move || destruct_callback(&f_for_dtor)));
Ok(f)
}
pub fn create_function_mut<F, A, R>(&'scope self, func: F) -> Result<Function>
where
F: FnMut(&Lua, A) -> Result<R> + 'scope,
A: FromLuaMulti,
R: IntoLuaMulti,
{
let func = RefCell::new(func);
self.create_function(move |lua, args| {
let mut borrow = func
.try_borrow_mut()
.map_err(|_| Error::runtime("mutable callback called recursively"))?;
(borrow)(lua, args)
})
}
pub fn create_userdata<T>(&'scope self, data: T) -> Result<AnyUserData>
where
T: UserData + 'env,
{
let (ud, neutralise) = create_scoped_userdata(&self.lua, data)?;
self.destructors.list.borrow_mut().push(neutralise);
Ok(ud)
}
pub fn add_destructor(&'scope self, destructor: impl FnOnce() + 'env) {
let destructor: Box<dyn FnOnce() + 'env> = Box::new(destructor);
let destructor: Box<dyn FnOnce()> =
unsafe { mem::transmute::<Box<dyn FnOnce() + 'env>, Box<dyn FnOnce()>>(destructor) };
self.destructors.list.borrow_mut().push(destructor);
}
}
impl Lua {
pub fn scope<'env, R>(
&self,
f: impl for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> Result<R>,
) -> Result<R> {
let scope = Scope::new(self.clone());
f(&scope)
}
}