116 lines
2.8 KiB
Rust
116 lines
2.8 KiB
Rust
#![doc(hidden)]
|
|
use derive_more::{Deref, DerefMut};
|
|
use luaffi::{Module, Registry};
|
|
use luajit::{Chunk, State};
|
|
use std::rc::Rc;
|
|
use tokio::{
|
|
task::{JoinHandle, LocalSet, futures::TaskLocalFuture, spawn_local},
|
|
task_local,
|
|
};
|
|
|
|
pub type ErrorFn = dyn Fn(&luajit::Error);
|
|
|
|
pub struct Builder {
|
|
registry: Registry,
|
|
report_err: Rc<ErrorFn>,
|
|
}
|
|
|
|
impl Builder {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
registry: Registry::new(),
|
|
report_err: Rc::new(|err| match err.trace() {
|
|
Some(trace) => eprintln!("unhandled lua error: {err}\n{trace}"),
|
|
None => eprintln!("unhandled lua error: {err}"),
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub fn registry(&self) -> &Registry {
|
|
&self.registry
|
|
}
|
|
|
|
pub fn unhandled_error(&mut self, handler: impl Fn(&luajit::Error) + 'static) -> &mut Self {
|
|
self.report_err = Rc::new(handler);
|
|
self
|
|
}
|
|
|
|
pub fn module<T: Module>(&mut self) -> &mut Self {
|
|
self.registry.preload::<T>();
|
|
self
|
|
}
|
|
|
|
pub fn build(&self) -> luajit::Result<Runtime> {
|
|
Ok(Runtime {
|
|
cx: Context {
|
|
state: {
|
|
let mut s = State::new()?;
|
|
s.eval(Chunk::new(self.registry.build()).path("[luby]"), 0, 0)?;
|
|
s
|
|
},
|
|
report_err: self.report_err.clone(),
|
|
},
|
|
tasks: LocalSet::new(),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Deref, DerefMut)]
|
|
pub struct Runtime {
|
|
#[deref]
|
|
#[deref_mut]
|
|
cx: Context,
|
|
tasks: LocalSet,
|
|
}
|
|
|
|
impl Runtime {
|
|
pub fn spawn<T: 'static>(
|
|
&self,
|
|
f: impl AsyncFnOnce(&mut Context) -> T + 'static,
|
|
) -> JoinHandle<T> {
|
|
self.tasks
|
|
.spawn_local(async move { f(&mut CURRENT.with(|s| s.new_thread())).await })
|
|
}
|
|
}
|
|
|
|
impl IntoFuture for Runtime {
|
|
type Output = ();
|
|
type IntoFuture = TaskLocalFuture<Context, LocalSet>;
|
|
|
|
fn into_future(self) -> Self::IntoFuture {
|
|
CURRENT.scope(self.cx, self.tasks)
|
|
}
|
|
}
|
|
|
|
task_local! {
|
|
static CURRENT: Context;
|
|
}
|
|
|
|
#[derive(Deref, DerefMut)]
|
|
pub struct Context {
|
|
#[deref]
|
|
#[deref_mut]
|
|
state: State,
|
|
report_err: Rc<ErrorFn>,
|
|
}
|
|
|
|
impl Context {
|
|
pub fn new_thread(&self) -> Self {
|
|
Self {
|
|
state: self.state.new_thread(),
|
|
report_err: self.report_err.clone(),
|
|
}
|
|
}
|
|
|
|
pub fn report_error(&self, err: &luajit::Error) {
|
|
(self.report_err)(&err);
|
|
}
|
|
}
|
|
|
|
pub fn spawn<T: 'static>(f: impl AsyncFnOnce(&mut Context) -> T + 'static) -> JoinHandle<T> {
|
|
// SAFETY: `new_thread` must be called inside `spawn_local` because this free-standing spawn
|
|
// function may be called via ffi from lua, and it is not safe to access the lua state within
|
|
// ffi calls.
|
|
spawn_local(async move { f(&mut CURRENT.with(|s| s.new_thread())).await })
|
|
}
|