2024-03-10 02:18:54 +01:00
|
|
|
use std::io::Write;
|
2024-08-06 23:02:28 +02:00
|
|
|
use std::sync::Mutex;
|
2024-03-10 02:18:54 +01:00
|
|
|
|
2024-08-15 22:19:08 +02:00
|
|
|
use crate::api::controller::ControllerCallback;
|
2024-08-05 19:16:17 +02:00
|
|
|
use crate::api::Cursor;
|
2024-03-10 02:18:54 +01:00
|
|
|
use crate::prelude::*;
|
2024-08-08 21:56:22 +02:00
|
|
|
use crate::workspace::worker::DetachResult;
|
2024-03-10 02:18:54 +01:00
|
|
|
use mlua::prelude::*;
|
2024-08-15 22:19:08 +02:00
|
|
|
use tokio::sync::mpsc;
|
2024-03-10 02:18:54 +01:00
|
|
|
|
2024-08-08 21:56:22 +02:00
|
|
|
impl From::<CodempError> for LuaError {
|
|
|
|
fn from(value: CodempError) -> Self {
|
2024-08-15 03:45:50 +02:00
|
|
|
LuaError::WithContext {
|
|
|
|
context: value.to_string(),
|
|
|
|
cause: std::sync::Arc::new(LuaError::external(value)),
|
|
|
|
}
|
2024-08-08 21:56:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
fn tokio() -> &'static tokio::runtime::Runtime {
|
|
|
|
use std::sync::OnceLock;
|
|
|
|
static RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
|
|
|
|
RT.get_or_init(||
|
|
|
|
tokio::runtime::Builder::new_current_thread()
|
|
|
|
.enable_all()
|
|
|
|
.build()
|
|
|
|
.expect("could not create tokio runtime")
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Promise<T: Send + Sync + IntoLuaMulti>(Option<tokio::task::JoinHandle<LuaResult<T>>>);
|
|
|
|
|
|
|
|
impl<T: Send + Sync + IntoLuaMulti + 'static> LuaUserData for Promise<T> {
|
|
|
|
fn add_fields<'a, F: LuaUserDataFields<'a, Self>>(fields: &mut F) {
|
|
|
|
fields.add_field_method_get("ready", |_, this|
|
|
|
|
Ok(this.0.as_ref().map_or(true, |x| x.is_finished()))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_methods<'a, M: LuaUserDataMethods<'a, Self>>(methods: &mut M) {
|
|
|
|
// TODO: await MUST NOT be used in callbacks!!
|
|
|
|
methods.add_method_mut("await", |_, this, ()| match this.0.take() {
|
|
|
|
None => Err(LuaError::runtime("Promise already awaited")),
|
|
|
|
Some(x) => {
|
|
|
|
tokio()
|
|
|
|
.block_on(x)
|
|
|
|
.map_err(LuaError::runtime)?
|
|
|
|
},
|
|
|
|
});
|
|
|
|
methods.add_method_mut("and_then", |_, this, (cb,):(LuaFunction,)| match this.0.take() {
|
|
|
|
None => Err(LuaError::runtime("Promise already awaited")),
|
|
|
|
Some(x) => {
|
|
|
|
tokio()
|
|
|
|
.spawn(async move {
|
|
|
|
match x.await {
|
|
|
|
Err(e) => tracing::error!("could not join promise to run callback: {e}"),
|
|
|
|
Ok(Err(e)) => tracing::error!("promise returned error: {e}"),
|
|
|
|
Ok(Ok(res)) => {
|
|
|
|
if let Err(e) = cb.call::<T,()>(res) {
|
|
|
|
tracing::error!("error running promise callback: {e}");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
});
|
|
|
|
Ok(())
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! a_sync {
|
|
|
|
($($clone:ident)* => $x:expr) => {
|
|
|
|
{
|
|
|
|
$(let $clone = $clone.clone();)*
|
|
|
|
Ok(Promise(Some(tokio().spawn(async move { $x }))))
|
|
|
|
}
|
|
|
|
};
|
2024-03-10 02:18:54 +01:00
|
|
|
}
|
|
|
|
|
2024-08-08 21:56:22 +02:00
|
|
|
fn runtime_drive_forever(_: &Lua, ():()) -> LuaResult<Driver> {
|
|
|
|
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
2024-08-17 00:06:57 +02:00
|
|
|
std::thread::spawn(move || tokio().block_on(async move {
|
|
|
|
tracing::info!(" :: driving runtime...");
|
2024-08-08 21:56:22 +02:00
|
|
|
tokio::select! {
|
|
|
|
() = std::future::pending::<()>() => {},
|
|
|
|
_ = rx.recv() => {},
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
Ok(Driver(tx))
|
2024-03-10 02:18:54 +01:00
|
|
|
}
|
|
|
|
|
2024-08-15 22:19:08 +02:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
struct Driver(tokio::sync::mpsc::UnboundedSender<()>);
|
|
|
|
impl LuaUserData for Driver {
|
|
|
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
|
|
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(format!("{:?}", this)));
|
|
|
|
methods.add_method("stop", |_, this, ()| Ok(this.0.send(()).is_ok()));
|
2024-08-08 04:14:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-15 22:19:08 +02:00
|
|
|
|
2024-08-08 02:48:21 +02:00
|
|
|
impl LuaUserData for CodempClient {
|
|
|
|
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
|
|
|
fields.add_field_method_get("id", |_, this| Ok(this.user_id().to_string()));
|
|
|
|
}
|
2024-03-10 02:18:54 +01:00
|
|
|
|
2024-08-08 02:48:21 +02:00
|
|
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
2024-08-15 22:19:08 +02:00
|
|
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(format!("{:?}", this)));
|
|
|
|
|
2024-08-08 02:48:21 +02:00
|
|
|
// join a remote workspace and start processing cursor events
|
2024-08-15 22:19:08 +02:00
|
|
|
methods.add_method("join_workspace", |_, this, (session,):(String,)|
|
2024-08-17 00:06:57 +02:00
|
|
|
a_sync! { this => Ok(this.join_workspace(&session).await?) }
|
2024-08-15 22:19:08 +02:00
|
|
|
);
|
2024-08-08 04:42:11 +02:00
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("leave_workspace", |_, this, (session,):(String,)|
|
2024-08-08 04:42:11 +02:00
|
|
|
Ok(this.leave_workspace(&session))
|
2024-08-17 00:06:57 +02:00
|
|
|
);
|
2024-08-08 02:48:21 +02:00
|
|
|
|
|
|
|
methods.add_method("get_workspace", |_, this, (session,):(String,)| Ok(this.get_workspace(&session)));
|
2024-08-15 22:19:08 +02:00
|
|
|
methods.add_method("active_workspaces", |_, this, ()| Ok(this.active_workspaces()));
|
2024-08-08 02:48:21 +02:00
|
|
|
}
|
2024-03-10 02:18:54 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-08-05 19:16:17 +02:00
|
|
|
impl LuaUserData for CodempWorkspace {
|
2024-03-10 02:18:54 +01:00
|
|
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
2024-08-15 22:19:08 +02:00
|
|
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(format!("{:?}", this)));
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("create_buffer", |_, this, (name,):(String,)|
|
|
|
|
a_sync! { this => Ok(this.create(&name).await?) }
|
|
|
|
);
|
2024-03-10 02:18:54 +01:00
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("attach", |_, this, (name,):(String,)|
|
|
|
|
a_sync! { this => Ok(this.attach(&name).await?) }
|
|
|
|
);
|
2024-03-10 02:18:54 +01:00
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("detach", |_, this, (name,):(String,)|
|
2024-08-08 04:42:11 +02:00
|
|
|
Ok(matches!(this.detach(&name), DetachResult::Detaching | DetachResult::AlreadyDetached))
|
2024-08-17 00:06:57 +02:00
|
|
|
);
|
2024-08-08 04:42:11 +02:00
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("delete_buffer", |_, this, (name,):(String,)|
|
|
|
|
a_sync! { this => Ok(this.delete(&name).await?) }
|
|
|
|
);
|
2024-03-10 02:18:54 +01:00
|
|
|
|
2024-08-05 19:16:17 +02:00
|
|
|
methods.add_method("get_buffer", |_, this, (name,):(String,)| Ok(this.buffer_by_name(&name)));
|
2024-08-15 03:42:25 +02:00
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("event", |_, this, ()|
|
|
|
|
a_sync! { this => Ok(this.event().await?) }
|
|
|
|
);
|
|
|
|
|
|
|
|
methods.add_method("fetch_buffers", |_, this, ()|
|
|
|
|
a_sync! { this => Ok(this.fetch_buffers().await?) }
|
|
|
|
);
|
|
|
|
methods.add_method("fetch_users", |_, this, ()|
|
|
|
|
a_sync! { this => Ok(this.fetch_users().await?) }
|
|
|
|
);
|
2024-08-15 22:19:08 +02:00
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("callback", |_, this, (cb,):(LuaFunction,)| {
|
|
|
|
let _this = this.clone();
|
|
|
|
tokio().spawn(async move {
|
|
|
|
while let Ok(ev) = _this.event().await {
|
|
|
|
if let Err(e) = cb.call::<CodempEvent,()>(ev) {
|
|
|
|
tracing::error!("error running workspace callback: {e}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
Ok(())
|
|
|
|
});
|
2024-03-10 02:18:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
2024-08-15 22:19:08 +02:00
|
|
|
fields.add_field_method_get("id", |_, this| Ok(this.id()));
|
2024-08-05 19:16:17 +02:00
|
|
|
fields.add_field_method_get("cursor", |_, this| Ok(this.cursor()));
|
|
|
|
fields.add_field_method_get("filetree", |_, this| Ok(this.filetree()));
|
2024-08-15 22:19:08 +02:00
|
|
|
fields.add_field_method_get("active_buffers", |_, this| Ok(this.buffer_list()));
|
2024-03-10 02:18:54 +01:00
|
|
|
// fields.add_field_method_get("users", |_, this| Ok(this.0.users())); // TODO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-15 03:42:25 +02:00
|
|
|
impl LuaUserData for CodempEvent {
|
2024-08-15 22:19:08 +02:00
|
|
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
|
|
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(format!("{:?}", this)));
|
|
|
|
}
|
|
|
|
|
2024-08-15 03:42:25 +02:00
|
|
|
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
|
|
|
fields.add_field_method_get("type", |_, this| match this {
|
2024-08-17 00:06:57 +02:00
|
|
|
CodempEvent::FileTreeUpdated(_) => Ok("filetree"),
|
2024-08-15 03:42:25 +02:00
|
|
|
CodempEvent::UserJoin(_) | CodempEvent::UserLeave(_) => Ok("user"),
|
|
|
|
});
|
|
|
|
fields.add_field_method_get("value", |_, this| match this {
|
2024-08-17 00:06:57 +02:00
|
|
|
CodempEvent::FileTreeUpdated(x) => Ok(x.clone()),
|
|
|
|
CodempEvent::UserJoin(x) | CodempEvent::UserLeave(x) => Ok(x.clone()),
|
2024-08-15 03:42:25 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-05 19:16:17 +02:00
|
|
|
impl LuaUserData for CodempCursorController {
|
2024-03-10 02:18:54 +01:00
|
|
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
2024-08-05 19:16:17 +02:00
|
|
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(format!("{:?}", this)));
|
2024-08-15 22:19:08 +02:00
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("send", |_, this, (buffer, start_row, start_col, end_row, end_col):(String, i32, i32, i32, i32)|
|
|
|
|
a_sync! { this => Ok(this.send(CodempCursor { buffer, start: (start_row, start_col), end: (end_row, end_col), user: None }).await?) }
|
|
|
|
);
|
|
|
|
methods.add_method("try_recv", |_, this, ()|
|
|
|
|
a_sync! { this => Ok(this.try_recv().await?) }
|
|
|
|
);
|
|
|
|
methods.add_method("recv", |_, this, ()| a_sync! { this => Ok(this.recv().await?) });
|
|
|
|
methods.add_method("poll", |_, this, ()| a_sync! { this => Ok(this.poll().await?) });
|
2024-08-15 22:19:08 +02:00
|
|
|
|
|
|
|
methods.add_method("stop", |_, this, ()| Ok(this.stop()));
|
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("clear_callback", |_, this, ()| { this.clear_callback(); Ok(()) });
|
2024-08-15 22:19:08 +02:00
|
|
|
methods.add_method("callback", |_, this, (cb,):(LuaFunction,)| {
|
2024-08-17 00:06:57 +02:00
|
|
|
this.callback(ControllerCallback::from(move |controller: CodempCursorController| {
|
|
|
|
if let Err(e) = cb.call::<(CodempCursorController,), ()>((controller.clone(),)) {
|
2024-08-15 22:19:08 +02:00
|
|
|
tracing::error!("error running cursor callback: {e}");
|
|
|
|
}
|
|
|
|
}));
|
2024-03-10 02:18:54 +01:00
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-05 19:16:17 +02:00
|
|
|
impl LuaUserData for Cursor {
|
2024-08-15 22:19:08 +02:00
|
|
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
|
|
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(format!("{:?}", this)));
|
|
|
|
}
|
|
|
|
|
2024-03-10 02:18:54 +01:00
|
|
|
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
2024-08-05 19:16:17 +02:00
|
|
|
fields.add_field_method_get("user", |_, this| Ok(this.user.map(|x| x.to_string())));
|
|
|
|
fields.add_field_method_get("buffer", |_, this| Ok(this.buffer.clone()));
|
|
|
|
fields.add_field_method_get("start", |_, this| Ok(RowCol::from(this.start)));
|
|
|
|
fields.add_field_method_get("finish", |_, this| Ok(RowCol::from(this.end)));
|
2024-03-10 02:18:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-08 21:56:22 +02:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
2024-08-08 02:48:21 +02:00
|
|
|
struct RowCol {
|
|
|
|
row: i32,
|
|
|
|
col: i32,
|
2024-08-05 19:16:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<(i32, i32)> for RowCol {
|
|
|
|
fn from((row, col): (i32, i32)) -> Self {
|
|
|
|
Self { row, col }
|
2024-03-10 02:18:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-05 19:16:17 +02:00
|
|
|
impl LuaUserData for RowCol {
|
2024-08-15 22:19:08 +02:00
|
|
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
|
|
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(format!("{:?}", this)));
|
|
|
|
}
|
|
|
|
|
2024-08-05 19:16:17 +02:00
|
|
|
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
|
|
|
fields.add_field_method_get("row", |_, this| Ok(this.row));
|
|
|
|
fields.add_field_method_get("col", |_, this| Ok(this.col));
|
|
|
|
}
|
|
|
|
}
|
2024-03-10 02:18:54 +01:00
|
|
|
|
2024-08-05 19:16:17 +02:00
|
|
|
impl LuaUserData for CodempBufferController {
|
2024-03-10 02:18:54 +01:00
|
|
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
2024-08-05 19:16:17 +02:00
|
|
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(format!("{:?}", this)));
|
2024-08-15 22:19:08 +02:00
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("send", |_, this, (start, end, content, hash): (usize, usize, String, Option<i64>)|
|
|
|
|
a_sync! { this => Ok(
|
|
|
|
this.send(
|
2024-03-10 02:18:54 +01:00
|
|
|
CodempTextChange {
|
2024-08-08 02:48:21 +02:00
|
|
|
start: start as u32,
|
|
|
|
end: end as u32,
|
2024-08-17 00:06:57 +02:00
|
|
|
content,
|
|
|
|
hash,
|
2024-03-10 02:18:54 +01:00
|
|
|
}
|
2024-08-17 00:06:57 +02:00
|
|
|
).await?
|
|
|
|
)}
|
|
|
|
);
|
2024-08-15 22:19:08 +02:00
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("try_recv", |_, this, ()| a_sync! { this => Ok(this.try_recv().await?) });
|
|
|
|
methods.add_method("recv", |_, this, ()| a_sync! { this => Ok(this.recv().await?) });
|
|
|
|
methods.add_method("poll", |_, this, ()| a_sync! { this => Ok(this.poll().await?) });
|
2024-08-15 22:19:08 +02:00
|
|
|
|
|
|
|
methods.add_method("stop", |_, this, ()| Ok(this.stop()));
|
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("content", |_, this, ()| a_sync! { this => Ok(this.content().await?) });
|
2024-08-15 22:19:08 +02:00
|
|
|
|
2024-08-17 00:06:57 +02:00
|
|
|
methods.add_method("clear_callback", |_, this, ()| { this.clear_callback(); Ok(()) });
|
2024-08-15 22:19:08 +02:00
|
|
|
methods.add_method("callback", |_, this, (cb,):(LuaFunction,)| {
|
2024-08-15 22:47:49 +02:00
|
|
|
this.callback(move |controller: CodempBufferController| {
|
2024-08-15 22:19:08 +02:00
|
|
|
let _c = controller.clone();
|
2024-08-17 00:06:57 +02:00
|
|
|
if let Err(e) = cb.call::<(CodempBufferController,), ()>((_c,)) {
|
|
|
|
tracing::error!("error running buffer#{} callback: {e}", controller.name());
|
2024-08-15 22:19:08 +02:00
|
|
|
}
|
2024-08-15 22:47:49 +02:00
|
|
|
});
|
2024-03-10 02:18:54 +01:00
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-05 19:16:17 +02:00
|
|
|
impl LuaUserData for CodempTextChange {
|
2024-03-10 02:18:54 +01:00
|
|
|
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
2024-08-05 19:16:17 +02:00
|
|
|
fields.add_field_method_get("content", |_, this| Ok(this.content.clone()));
|
2024-08-08 02:48:21 +02:00
|
|
|
fields.add_field_method_get("first", |_, this| Ok(this.start));
|
|
|
|
fields.add_field_method_get("last", |_, this| Ok(this.end));
|
2024-08-14 18:07:26 +02:00
|
|
|
fields.add_field_method_get("hash", |_, this| Ok(this.hash));
|
2024-03-10 02:18:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
2024-08-05 19:16:17 +02:00
|
|
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(format!("{:?}", this)));
|
|
|
|
methods.add_method("apply", |_, this, (txt,):(String,)| Ok(this.apply(&txt)));
|
2024-03-10 02:18:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// define module and exports
|
|
|
|
#[mlua::lua_module]
|
2024-08-06 23:01:44 +02:00
|
|
|
fn codemp_lua(lua: &Lua) -> LuaResult<LuaTable> {
|
2024-03-10 02:18:54 +01:00
|
|
|
let exports = lua.create_table()?;
|
|
|
|
|
2024-08-08 02:48:21 +02:00
|
|
|
// entrypoint
|
2024-08-15 22:19:08 +02:00
|
|
|
exports.set("connect", lua.create_function(|_, (host, username, password):(String,String,String)|
|
2024-08-17 00:06:57 +02:00
|
|
|
a_sync! { => Ok(CodempClient::new(host, username, password).await?) }
|
2024-08-15 22:19:08 +02:00
|
|
|
)?)?;
|
2024-08-08 02:48:21 +02:00
|
|
|
|
2024-08-14 17:16:58 +02:00
|
|
|
// utils
|
2024-08-15 22:19:08 +02:00
|
|
|
exports.set("hash", lua.create_function(|_, (txt,):(String,)|
|
|
|
|
Ok(crate::hash(txt))
|
|
|
|
)?)?;
|
2024-08-14 17:16:58 +02:00
|
|
|
|
2024-08-08 02:48:21 +02:00
|
|
|
// runtime
|
2024-08-08 04:02:56 +02:00
|
|
|
exports.set("runtime_drive_forever", lua.create_function(runtime_drive_forever)?)?;
|
2024-08-08 02:48:21 +02:00
|
|
|
|
|
|
|
// logging
|
2024-08-15 22:19:08 +02:00
|
|
|
exports.set("logger", lua.create_function(logger)?)?;
|
2024-03-10 02:18:54 +01:00
|
|
|
|
|
|
|
Ok(exports)
|
|
|
|
}
|
|
|
|
|
2024-08-15 22:19:08 +02:00
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
struct LuaLoggerProducer(mpsc::UnboundedSender<String>);
|
|
|
|
impl Write for LuaLoggerProducer {
|
|
|
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
|
|
|
let _ = self.0.send(String::from_utf8_lossy(buf).to_string());
|
|
|
|
Ok(buf.len())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO can we make this less verbose?
|
|
|
|
fn logger(_: &Lua, (printer, debug): (LuaValue, Option<bool>)) -> LuaResult<bool> {
|
|
|
|
let level = if debug.unwrap_or_default() { tracing::Level::DEBUG } else {tracing::Level::INFO };
|
|
|
|
let success = match printer {
|
|
|
|
LuaNil
|
|
|
|
| LuaValue::Boolean(_)
|
|
|
|
| LuaValue::LightUserData(_)
|
|
|
|
| LuaValue::Integer(_)
|
|
|
|
| LuaValue::Number(_)
|
|
|
|
| LuaValue::Table(_)
|
|
|
|
| LuaValue::Thread(_)
|
|
|
|
| LuaValue::UserData(_)
|
|
|
|
| LuaValue::Error(_) => return Err(LuaError::BindError), // TODO full BadArgument type??
|
|
|
|
LuaValue::String(path) => {
|
|
|
|
let logfile = std::fs::File::create(path.to_string_lossy()).map_err(|e| LuaError::RuntimeError(e.to_string()))?;
|
|
|
|
let format = tracing_subscriber::fmt::format()
|
|
|
|
.with_level(true)
|
|
|
|
.with_target(true)
|
|
|
|
.with_thread_ids(true)
|
|
|
|
.with_thread_names(true)
|
|
|
|
.with_ansi(false)
|
|
|
|
.with_file(false)
|
|
|
|
.with_line_number(false)
|
|
|
|
.with_source_location(false);
|
|
|
|
tracing_subscriber::fmt()
|
|
|
|
.event_format(format)
|
|
|
|
.with_max_level(level)
|
|
|
|
.with_writer(Mutex::new(logfile))
|
|
|
|
.try_init()
|
|
|
|
.is_ok()
|
|
|
|
},
|
|
|
|
LuaValue::Function(cb) => {
|
|
|
|
let (tx, mut rx) = mpsc::unbounded_channel();
|
|
|
|
let format = tracing_subscriber::fmt::format()
|
|
|
|
.with_level(true)
|
|
|
|
.with_target(true)
|
|
|
|
.with_thread_ids(false)
|
|
|
|
.with_thread_names(false)
|
|
|
|
.with_ansi(false)
|
|
|
|
.with_file(false)
|
|
|
|
.with_line_number(false)
|
|
|
|
.with_source_location(false);
|
|
|
|
let res = tracing_subscriber::fmt()
|
|
|
|
.event_format(format)
|
|
|
|
.with_max_level(level)
|
|
|
|
.with_writer(Mutex::new(LuaLoggerProducer(tx)))
|
|
|
|
.try_init()
|
|
|
|
.is_ok();
|
|
|
|
if res {
|
2024-08-17 00:06:57 +02:00
|
|
|
tokio().spawn(async move {
|
2024-08-15 22:19:08 +02:00
|
|
|
while let Some(msg) = rx.recv().await {
|
|
|
|
let _ = cb.call::<(String,),()>((msg,));
|
|
|
|
// if the logger fails logging who logs it?
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
res
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(success)
|
|
|
|
}
|