codemp/src/ffi/lua/ext/callback.rs

110 lines
4.3 KiB
Rust
Raw Normal View History

2024-09-17 23:00:30 +02:00
use mlua_codemp_patch as mlua;
use mlua::prelude::*;
use crate::prelude::*;
use crate::ext::IgnorableError;
pub(crate) fn callback() -> &'static CallbackChannel<LuaCallback> {
static CHANNEL: std::sync::OnceLock<CallbackChannel<LuaCallback>> = std::sync::OnceLock::new();
CHANNEL.get_or_init(CallbackChannel::default)
2024-09-17 23:00:30 +02:00
}
pub(crate) struct CallbackChannel<T> {
tx: std::sync::Arc<tokio::sync::mpsc::UnboundedSender<T>>,
rx: std::sync::Mutex<tokio::sync::mpsc::UnboundedReceiver<T>>
2024-09-17 23:00:30 +02:00
}
impl Default for CallbackChannel<LuaCallback> {
2024-09-17 23:00:30 +02:00
fn default() -> Self {
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
let rx = std::sync::Mutex::new(rx);
Self {
tx: std::sync::Arc::new(tx),
rx,
}
}
}
impl CallbackChannel<LuaCallback> {
pub(crate) fn invoke(&self, cb: LuaFunction, arg: impl Into<CallbackArg>) {
self.tx.send(LuaCallback::Invoke(cb, arg.into()))
2024-09-17 23:00:30 +02:00
.unwrap_or_warn("error scheduling callback")
}
pub(crate) fn failure(&self, err: impl std::error::Error) {
self.tx.send(LuaCallback::Fail(format!("promise failed with error: {err:?}")))
.unwrap_or_warn("error scheduling callback failure")
}
pub(crate) fn recv(&self) -> Option<LuaCallback> {
2024-09-17 23:00:30 +02:00
match self.rx.try_lock() {
Err(e) => {
tracing::debug!("backing off from callback mutex: {e}");
2024-09-17 23:00:30 +02:00
None
},
Ok(mut lock) => match lock.try_recv() {
Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
tracing::error!("callback channel closed");
None
},
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => None,
Ok(cb) => Some(cb),
2024-09-17 23:00:30 +02:00
},
}
}
}
pub(crate) enum LuaCallback {
Fail(String),
Invoke(LuaFunction, CallbackArg),
}
2024-09-17 23:00:30 +02:00
pub(crate) enum CallbackArg {
Nil,
Str(String),
VecStr(Vec<String>),
Client(CodempClient),
CursorController(CodempCursorController),
BufferController(CodempBufferController),
Workspace(CodempWorkspace),
Event(CodempEvent),
Cursor(CodempCursor),
MaybeCursor(Option<CodempCursor>),
TextChange(CodempTextChange),
MaybeTextChange(Option<CodempTextChange>),
}
impl IntoLua for CallbackArg {
// TODO this basically calls .into_lua() on all enum variants
// i wish i could do this with a Box<dyn IntoLua> or an impl IntoLua
// but IntoLua requires Sized so it can't be made into an object
fn into_lua(self, lua: &Lua) -> LuaResult<LuaValue> {
match self {
CallbackArg::Nil => Ok(LuaValue::Nil),
CallbackArg::Str(x) => x.into_lua(lua),
CallbackArg::Client(x) => x.into_lua(lua),
CallbackArg::CursorController(x) => x.into_lua(lua),
CallbackArg::BufferController(x) => x.into_lua(lua),
CallbackArg::Workspace(x) => x.into_lua(lua),
CallbackArg::VecStr(x) => x.into_lua(lua),
CallbackArg::Event(x) => x.into_lua(lua),
CallbackArg::Cursor(x) => x.into_lua(lua),
CallbackArg::MaybeCursor(x) => x.into_lua(lua),
CallbackArg::TextChange(x) => x.into_lua(lua),
CallbackArg::MaybeTextChange(x) => x.into_lua(lua),
}
}
}
impl From<()> for CallbackArg { fn from(_: ()) -> Self { CallbackArg::Nil } }
impl From<String> for CallbackArg { fn from(value: String) -> Self { CallbackArg::Str(value) } }
impl From<CodempClient> for CallbackArg { fn from(value: CodempClient) -> Self { CallbackArg::Client(value) } }
impl From<CodempCursorController> for CallbackArg { fn from(value: CodempCursorController) -> Self { CallbackArg::CursorController(value) } }
impl From<CodempBufferController> for CallbackArg { fn from(value: CodempBufferController) -> Self { CallbackArg::BufferController(value) } }
impl From<CodempWorkspace> for CallbackArg { fn from(value: CodempWorkspace) -> Self { CallbackArg::Workspace(value) } }
impl From<Vec<String>> for CallbackArg { fn from(value: Vec<String>) -> Self { CallbackArg::VecStr(value) } }
impl From<CodempEvent> for CallbackArg { fn from(value: CodempEvent) -> Self { CallbackArg::Event(value) } }
impl From<CodempCursor> for CallbackArg { fn from(value: CodempCursor) -> Self { CallbackArg::Cursor(value) } }
impl From<Option<CodempCursor>> for CallbackArg { fn from(value: Option<CodempCursor>) -> Self { CallbackArg::MaybeCursor(value) } }
impl From<CodempTextChange> for CallbackArg { fn from(value: CodempTextChange) -> Self { CallbackArg::TextChange(value) } }
impl From<Option<CodempTextChange>> for CallbackArg { fn from(value: Option<CodempTextChange>) -> Self { CallbackArg::MaybeTextChange(value) } }