use core::marker::PhantomData;
use std::cell::RefCell;
use crate::callback::{Arguments, CallbackReturn};
use crate::error::Error;
use crate::function::Function;
use crate::function::callback::{
CallbackFn as FunctionCallbackFn, CallbackMut as FunctionCallbackMut, ScopedCallback,
create_scoped as create_scoped_callback,
};
use crate::lua::{Lua, LuaRef};
pub struct Scope<'scope, 'env: 'scope> {
lua: LuaRef<'env>,
callbacks: RefCell<Vec<ScopedCallback<'env>>>,
destructors: Destructors<'env>,
_scope_invariant: PhantomData<&'scope mut &'scope ()>,
_env_invariant: PhantomData<&'env mut &'env ()>,
}
struct Destructors<'env>(RefCell<Vec<Box<dyn FnOnce() + 'env>>>);
impl Lua {
#[allow(clippy::let_and_return)]
pub fn scope<'env, R>(
&'env self,
run: impl for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> Result<R, Error>,
) -> Result<R, Error> {
let result = run(&Scope::new(self.lua_ref()));
result
}
}
impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
fn new(lua: LuaRef<'env>) -> Self {
Self {
lua,
callbacks: RefCell::new(Vec::new()),
destructors: Destructors(RefCell::new(Vec::new())),
_scope_invariant: PhantomData,
_env_invariant: PhantomData,
}
}
pub fn create_function<F>(&'scope self, function: F) -> Result<Function<'scope>, Error>
where
F: for<'call> Fn(LuaRef<'call>, Arguments<'call>) -> Result<CallbackReturn<'call>, Error>
+ 'scope,
{
self.create_callback(FunctionCallbackFn::new(function))
}
pub fn create_function_mut<F>(&'scope self, function: F) -> Result<Function<'scope>, Error>
where
F: for<'call> FnMut(
LuaRef<'call>,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'scope,
{
self.create_callback(FunctionCallbackMut::new(function))
}
pub fn add_destructor(&self, destructor: impl FnOnce() + 'env) {
self.destructors.0.borrow_mut().push(Box::new(destructor));
}
fn create_callback<F>(&'scope self, callback: F) -> Result<Function<'scope>, Error>
where
F: crate::callback::Callback + 'scope,
{
let scoped = unsafe { create_scoped_callback(&self.lua, Box::new(callback))? };
let function: Function<'scope> = scoped.function().try_clone()?;
self.callbacks.borrow_mut().push(scoped);
Ok(function)
}
}
impl Drop for Destructors<'_> {
fn drop(&mut self) {
while let Some(destructor) = self.0.get_mut().pop() {
destructor();
}
}
}