diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e94a7f8..2991d28 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -15,15 +15,21 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install libudev-sys - run: sudo apt-get install -y libudev-dev + - name: Install libudev-sys and Lua5.4 + run: sudo apt-get install -y libudev-dev liblua5.4-dev - name: Build run: cargo build --verbose build-windows: name: Check on Windows runs-on: windows-latest + defaults: + run: + shell: msys2 {0} steps: + - uses: msys2/setup-msys2@v2 - uses: actions/checkout@v3 + - name: Install Rust & Lua + run: pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config - name: Build run: cargo build --verbose build-macos: @@ -31,5 +37,7 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v3 + - name: Install Lua5.4 + run: brew install lua - name: Build run: cargo build --verbose diff --git a/Cargo.lock b/Cargo.lock index 8e971a1..431868a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -633,6 +633,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "mlua" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "868d02cb5eb97761bbf6bd6922c1c7a88b8ea252bbf43bd8350a0bf8497a1fc0" +dependencies = [ + "bstr", + "futures-util", + "mlua-sys", + "num-traits", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "mlua-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2847b42764435201d8cbee1f517edb79c4cca4181877b90047587c89e1b7bce4" +dependencies = [ + "cc", + "cfg-if", + "pkg-config", +] + [[package]] name = "nix" version = "0.26.4" @@ -851,36 +876,18 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" -[[package]] -name = "rlua" -version = "0.19.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3120d9610f84b17da849f5cc8c089bb74299285515bb4a6114550bbc39ddb1e7" -dependencies = [ - "bitflags 2.4.2", - "bstr", - "libc", - "num-traits", - "rlua-lua54-sys", -] - -[[package]] -name = "rlua-lua54-sys" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aafabafe1895cb4a2be81a56d7ff3d46bf4b5d2f9cfdbea2ed404cdabe96474" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustversion" version = "1.0.14" @@ -904,10 +911,10 @@ dependencies = [ "ctrlc", "futures", "homedir", + "mlua", "rand", "ratatui", "regex", - "rlua", "serde", "serde_yaml", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 7e5b486..191145d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ serde = "1.0" serde_yaml = "0.9" rand = "0.8.5" clap = { version = "4.1.9", features = ["derive"] } -rlua = "0.19.8" +mlua = { version = "0.9.6", features = ["lua54", "async", "send"] } anyhow = "1.0.79" homedir = "0.2.1" regex = { version = "1.10.3", features = [] } diff --git a/src/plugin.rs b/src/plugin.rs index c2e8e65..75a93e5 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -3,7 +3,7 @@ use crate::text::TextView; use anyhow::Result; use chrono::Local; use homedir::get_my_home; -use rlua::{Context, Function, Lua, RegistryKey, Table, Thread}; +use mlua::{Function, Lua, RegistryKey, Table, Thread}; use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; @@ -120,7 +120,8 @@ impl Plugin { .to_string(); let code = std::fs::read_to_string(filepath).map_err(|_| "Cannot read plugin file")?; - let lua = Lua::new(); + let lua = Lua::new_with(mlua::StdLib::ALL_SAFE, mlua::LuaOptions::default()) + .map_err(|_| "Cannot create Lua obj".to_string())?; Plugin::check_integrity(&lua, &code)?; @@ -151,27 +152,32 @@ impl Plugin { self.name.as_str() } + fn create_lua_thread( + lua: &Lua, + code: &str, + coroutine_name: &str, + ) -> Result { + Plugin::append_plugins_dir(lua)?; + + lua.load(code) + .exec() + .map_err(|_| "Fail to load Lua code".to_string())?; + + let serial_rx: Thread = lua + .load(format!("coroutine.create({})", coroutine_name)) + .eval() + .map_err(|_| format!("Fail to create coroutine for {}", coroutine_name))?; + let reg = lua + .create_registry_value(serial_rx) + .map_err(|_| format!("Fail to create register for {} coroutines", coroutine_name))?; + Ok(reg) + } + pub fn serial_rx_call(&self, msg: Vec) -> SerialRxCall { - let lua = Lua::new(); - let code = self.code.as_str(); - - let serial_rx_reg: Result = lua.context(move |lua_ctx| { - Plugin::append_plugins_dir(&lua_ctx)?; - - lua_ctx - .load(code) - .exec() - .map_err(|_| "Fail to load Lua code".to_string())?; - - let serial_rx: Thread = lua_ctx - .load(r#"coroutine.create(serial_rx)"#) - .eval() - .map_err(|_| "Fail to create coroutine for serial_rx".to_string())?; - let reg = lua_ctx - .create_registry_value(serial_rx) - .map_err(|_| "Fail to create register for serial_rx coroutines".to_string())?; - Ok(reg) - }); + let lua = Lua::new_with(mlua::StdLib::ALL_SAFE, mlua::LuaOptions::default()) + .expect("Cannot create Lua obj"); + + let serial_rx_reg = Self::create_lua_thread(&lua, self.code.as_str(), "serial_rx"); SerialRxCall { lua, @@ -182,26 +188,10 @@ impl Plugin { } pub fn user_command_call(&self, arg_list: Vec) -> UserCommandCall { - let lua = Lua::new(); - let code = self.code.as_str(); - - let user_command_reg: Result = lua.context(move |lua_ctx| { - Plugin::append_plugins_dir(&lua_ctx)?; - - lua_ctx - .load(code) - .exec() - .map_err(|_| "Fail to load Lua code".to_string())?; - - let user_command: Thread = lua_ctx - .load(r#"coroutine.create(user_command)"#) - .eval() - .map_err(|_| "Fail to create coroutine for user_command".to_string())?; - let reg = lua_ctx - .create_registry_value(user_command) - .map_err(|_| "Fail to create register for user_command coroutines".to_string())?; - Ok(reg) - }); + let lua = Lua::new_with(mlua::StdLib::ALL_SAFE, mlua::LuaOptions::default()) + .expect("Cannot create Lua obj"); + + let user_command_reg = Self::create_lua_thread(&lua, self.code.as_str(), "user_command"); UserCommandCall { lua, @@ -211,7 +201,7 @@ impl Plugin { } } - fn append_plugins_dir(lua_ctx: &Context) -> Result<(), String> { + fn append_plugins_dir(lua: &Lua) -> Result<(), String> { let home_dir = get_my_home() .expect("Cannot get home directory") .expect("Cannot get home directory") @@ -219,7 +209,7 @@ impl Plugin { .expect("Cannot get home directory") .to_string(); - if lua_ctx + if lua .load( format!( "package.path = package.path .. ';{}/.config/scope/plugins/?.lua'", @@ -237,33 +227,30 @@ impl Plugin { } fn check_integrity(lua: &Lua, code: &str) -> Result<(), String> { - lua.context(|lua_ctx| { - let globals = lua_ctx.globals(); + let globals = lua.globals(); - Plugin::append_plugins_dir(&lua_ctx)?; + Plugin::append_plugins_dir(&lua)?; - lua_ctx - .load(code) - .exec() - .map_err(|_| "Fail to load Lua code".to_string())?; + lua.load(code) + .exec() + .map_err(|_| "Fail to load Lua code".to_string())?; - globals - .get::<_, Function>("serial_rx") - .map_err(|_| "serial_rx function not found in Lua code")?; - globals - .get::<_, Function>("user_command") - .map_err(|_| "user_command function not found in Lua code")?; + globals + .get::<_, Function>("serial_rx") + .map_err(|_| "serial_rx function not found in Lua code")?; + globals + .get::<_, Function>("user_command") + .map_err(|_| "user_command function not found in Lua code")?; - Ok(()) - }) + Ok(()) } } -fn resume_lua_thread rlua::ToLuaMulti<'a> + Send>( - thread: &Thread, - data: T, -) -> Option { - match thread.resume::(data) { +fn resume_lua_thread(thread: &Thread, data: T) -> Option +where + T: for<'a> mlua::IntoLuaMulti<'a>, +{ + match thread.resume::<_, Table>(data) { Ok(req) => { let req: PluginRequest = match req.try_into() { Ok(req) => req, @@ -283,30 +270,29 @@ impl Iterator for SerialRxCall { let thread = &self.thread; let msg = self.msg.clone(); - self.lua.context(move |lua_ctx| { - let serial_rx: Thread = lua_ctx - .registry_value(thread) - .expect("Cannot get serial_rx register"); + let serial_rx: Thread = self + .lua + .registry_value(thread) + .expect("Cannot get serial_rx register"); - let Some(req_result) = req_result else { - return resume_lua_thread(&serial_rx, msg); - }; + let Some(req_result) = req_result else { + return resume_lua_thread(&serial_rx, msg); + }; - match req_result { - PluginRequestResult::Exec { stdout, stderr } => { - match serial_rx.resume::<_, Table>((msg, stdout, stderr)) { - Ok(req) => { - let req: PluginRequest = match req.try_into() { - Ok(req) => req, - Err(msg) => return Some(PluginRequest::Eprintln { msg }), - }; - Some(req) - } - Err(_) => None, + match req_result { + PluginRequestResult::Exec { stdout, stderr } => { + match serial_rx.resume::<_, Table>((msg, stdout, stderr)) { + Ok(req) => { + let req: PluginRequest = match req.try_into() { + Ok(req) => req, + Err(msg) => return Some(PluginRequest::Eprintln { msg }), + }; + Some(req) } + Err(_) => None, } } - }) + } } } @@ -318,30 +304,29 @@ impl Iterator for UserCommandCall { let thread = &self.thread; let arg_list = self.arg_list.clone(); - self.lua.context(move |lua_ctx| { - let user_command: Thread = lua_ctx - .registry_value(thread) - .expect("Cannot get user_command register"); + let user_command: Thread = self + .lua + .registry_value(thread) + .expect("Cannot get user_command register"); - let Some(req_result) = req_result else { - return resume_lua_thread(&user_command, arg_list); - }; + let Some(req_result) = req_result else { + return resume_lua_thread(&user_command, arg_list); + }; - match req_result { - PluginRequestResult::Exec { stdout, stderr } => { - match user_command.resume::<_, Table>((arg_list, stdout, stderr)) { - Ok(req) => { - let req: PluginRequest = match req.try_into() { - Ok(req) => req, - Err(msg) => return Some(PluginRequest::Eprintln { msg }), - }; - Some(req) - } - Err(_) => None, + match req_result { + PluginRequestResult::Exec { stdout, stderr } => { + match user_command.resume::<_, Table>((arg_list, stdout, stderr)) { + Ok(req) => { + let req: PluginRequest = match req.try_into() { + Ok(req) => req, + Err(msg) => return Some(PluginRequest::Eprintln { msg }), + }; + Some(req) } + Err(_) => None, } } - }) + } } }