yazi/yazi-plugin/src/utils/sync.rs
2024-11-08 20:21:32 +08:00

79 lines
2.5 KiB
Rust

use mlua::{ExternalError, ExternalResult, Function, Lua, MultiValue, Table, Value};
use tokio::sync::oneshot;
use yazi_dds::Sendable;
use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}};
use yazi_shared::event::Data;
use super::Utils;
use crate::{loader::LOADER, runtime::RtRef};
impl Utils {
pub(super) fn sync(lua: &'static Lua, ya: &Table) -> mlua::Result<()> {
ya.raw_set(
"sync",
lua.create_function(|lua, f: Function| {
let mut rt = lua.named_registry_value::<RtRef>("rt")?;
if !rt.put_block(f.clone()) {
return Err("`ya.sync()` must be called in a plugin").into_lua_err();
}
let cur = rt.current().unwrap().to_owned();
lua.create_function(move |lua, mut args: MultiValue| {
args.push_front(Value::Table(LOADER.try_load(lua, &cur)?));
f.call::<MultiValue>(args)
})
})?,
)?;
Ok(())
}
pub(super) fn sync_isolate(lua: &Lua, ya: &Table) -> mlua::Result<()> {
ya.raw_set(
"sync",
lua.create_function(|lua, ()| {
let Some(block) = lua.named_registry_value::<RtRef>("rt")?.next_block() else {
return Err("`ya.sync()` must be called in a plugin").into_lua_err();
};
lua.create_async_function(move |lua, args: MultiValue| async move {
if let Some(cur) = lua.named_registry_value::<RtRef>("rt")?.current() {
Sendable::list_to_values(&lua, Self::retrieve(cur, block, args).await?)
} else {
Err("block spawned by `ya.sync()` must be called in a plugin").into_lua_err()
}
})
})?,
)?;
Ok(())
}
async fn retrieve(id: &str, calls: usize, args: MultiValue) -> mlua::Result<Vec<Data>> {
let args = Sendable::values_to_vec(args)?;
let (tx, rx) = oneshot::channel::<Vec<Data>>();
let callback: PluginCallback = {
let id = id.to_owned();
Box::new(move |lua, plugin| {
let Some(block) = lua.named_registry_value::<RtRef>("rt")?.get_block(&id, calls) else {
return Err("sync block not found".into_lua_err());
};
let args: Vec<_> = [Ok(Value::Table(plugin))]
.into_iter()
.chain(args.into_iter().map(|d| Sendable::data_to_value(lua, d)))
.collect::<mlua::Result<_>>()?;
// TODO: avoid unnecessary allocation
let values = Sendable::values_to_vec(block.call(MultiValue::from_iter(args))?)?;
tx.send(values).map_err(|_| "send failed".into_lua_err())
})
};
AppProxy::plugin(PluginOpt::new_callback(id, callback));
rx.await
.map_err(|_| format!("Failed to execute sync block-{calls} in `{id}` plugin").into_lua_err())
}
}