From f744553a0a5548cecbc774969376f15f12d925de Mon Sep 17 00:00:00 2001 From: Arijit Basu Date: Sun, 16 May 2021 23:18:47 +0530 Subject: [PATCH] Add support for native lua bindings Ref: https://github.com/sayanarijit/xplr/discussions/146#discussioncomment-741580 --- Cargo.lock | 59 ++++++++++++++++++++++++++++++++- Cargo.toml | 3 +- src/app.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++--- src/config.rs | 32 +++++++++++------- src/config.yml | 2 +- 5 files changed, 166 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96de2f5..b96910e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -126,6 +126,12 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + [[package]] name = "cfg-if" version = "1.0.0" @@ -332,6 +338,15 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "erased-serde" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0465971a8cc1fa2455c8465aaa377131e1f1cf4983280f474a13e68793aa770c" +dependencies = [ + "serde", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -488,6 +503,24 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lua-src" +version = "543.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029180f994b9b36f47d905f92569b516acf7d073778e2e781c15ee375b1ca27d" +dependencies = [ + "cc", +] + +[[package]] +name = "luajit-src" +version = "210.1.3+restyfe08842" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36d3de8377d8e0492b646527befb7eb826a9ecd2dc8c1f81ab0e654bc03a029b" +dependencies = [ + "cc", +] + [[package]] name = "maplit" version = "1.0.2" @@ -547,6 +580,23 @@ dependencies = [ "winapi", ] +[[package]] +name = "mlua" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd448d3e7018f2ff38dd732a374045f5b037eb4ee477d9241d9bb8c209528c1c" +dependencies = [ + "bstr", + "cc", + "erased-serde", + "lazy_static", + "lua-src", + "luajit-src", + "num-traits", + "pkg-config", + "serde", +] + [[package]] name = "natord" version = "1.0.9" @@ -677,6 +727,12 @@ dependencies = [ "sha-1", ] +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + [[package]] name = "plotters" version = "0.3.0" @@ -1193,7 +1249,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "xplr" -version = "0.9.1" +version = "0.10.0-beta.0" dependencies = [ "anyhow", "chrono", @@ -1205,6 +1261,7 @@ dependencies = [ "indexmap", "lazy_static", "mime_guess", + "mlua", "natord", "serde", "serde_yaml", diff --git a/Cargo.toml b/Cargo.toml index 54aace4..38154eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xplr" -version = "0.9.1" # Update config.yml, config.rs +version = "0.10.0-beta.0" # Update config.yml, config.rs authors = ["Arijit Basu "] edition = "2018" description = "A hackable, minimal, fast TUI file explorer" @@ -29,6 +29,7 @@ lazy_static = "1.4.0" indexmap = { version = "1.6.2", features = ["serde"] } natord = "1.0.9" humansize = "1.1.0" +mlua = { version = "0.5.4", features = ["luajit", "vendored", "serialize"] } [dev-dependencies] criterion = "0.3" diff --git a/src/app.rs b/src/app.rs index cac0d05..117bad2 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,6 +5,7 @@ use crate::ui::Layout; use anyhow::{bail, Result}; use chrono::{DateTime, Local}; use indexmap::set::IndexSet; +use mlua::LuaSerdeExt; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::collections::HashMap; @@ -1416,6 +1417,18 @@ impl History { } } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct LuaData { + config: Config, +} + +impl LuaData { + pub fn new(config: Config) -> Self { + Self { config } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct App { version: String, @@ -1442,14 +1455,16 @@ impl App { let config_dir = dirs::config_dir() .unwrap_or_else(|| PathBuf::from(".")) .join("xplr"); + let yaml_config_file = config_dir.join("config.yml"); + let lua_script_file = config_dir.join("init.lua"); - let config_file = config_dir.join("config.yml"); let default_config = Config::default(); let default_config_version = default_config.version().clone(); - let config: Config = if config_file.exists() { + // TODO deprecate this --------------------------------- + let config: Config = if yaml_config_file.exists() { let c: Config = - serde_yaml::from_reader(io::BufReader::new(&fs::File::open(&config_file)?))?; + serde_yaml::from_reader(io::BufReader::new(&fs::File::open(&yaml_config_file)?))?; c.extended() } else { default_config @@ -1461,7 +1476,59 @@ impl App { You config version is : {} Required version is : {} Visit {}", - config_file.to_string_lossy().to_string(), + yaml_config_file.to_string_lossy().to_string(), + config.version(), + default_config_version, + UPGRADE_GUIDE_LINK, + ) + }; + // ------------------------------------------------------- + + let config: Config = if lua_script_file.exists() { + let lua = mlua::Lua::new(); + let globals = lua.globals(); + let lua_script = fs::read_to_string(&lua_script_file)?; + let luadata = LuaData::new(config); + + // TODO: https://github.com/khvzak/mlua/issues/48 + + if let Err(e) = lua.to_value(&luadata).and_then(|v| globals.set("xplr", v)) { + bail!(e.to_string()) + }; + + if let Err(e) = lua + .load(&lua_script) + .set_name("init") + .and_then(|l| l.exec()) + { + bail!(e.to_string()) + } + + let version: String = match globals.get("version").and_then(|v| lua.from_value(v)) { + Ok(v) => v, + Err(_) => bail!(format!( + "'version' must globally be defined in {}", + &lua_script_file.to_string_lossy().to_string() + )), + }; + + let luadata: LuaData = match globals.get("xplr").and_then(|v| lua.from_value(v)) { + Ok(d) => d, + Err(e) => bail!(e.to_string()), + }; + + luadata.config.with_version(version) + } else { + config + }; + + if !config.is_compatible()? { + bail!( + "incompatible configuration version in {} + You config version is : {} + Required version is : {} + Visit {}", + lua_script_file.to_string_lossy().to_string(), config.version(), default_config_version, UPGRADE_GUIDE_LINK, @@ -1542,7 +1609,7 @@ impl App { if let Some(notif) = config.upgrade_notification()? { let notif = format!( - "{}. To stop seeing this log, update your config version from {} to {}.", + "{}. To stop seeing this log, update your config version from {} to {}", ¬if, config.version(), app.version() @@ -1553,6 +1620,18 @@ impl App { )); } + // if yaml_config_file.exists() && !lua_script_file.exists() { + // let notif = format!( + // "`config.yml` will be deprecated in favor of `init.lua`. To stop this warning, create empty file {}", + // lua_script_file.to_string_lossy().to_string() + // ); + + // app = app.enqueue(Task::new( + // MsgIn::External(ExternalMsg::LogWarning(notif)), + // None, + // )); + // } + app.write_pipes()?; Ok(app) } diff --git a/src/config.rs b/src/config.rs index 281dedd..92990a3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1259,7 +1259,7 @@ impl Config { self } - fn parsed_version(&self) -> Result<(u16, u16, u16)> { + fn parsed_version(&self) -> Result<(u16, u16, u16, Option)> { let mut configv = self .version .strip_prefix('v') @@ -1268,27 +1268,31 @@ impl Config { let major = configv.next().unwrap_or_default().parse::()?; let minor = configv.next().unwrap_or_default().parse::()?; - let bugfix = configv.next().unwrap_or_default().parse::()?; + let bugfix = configv + .next() + .and_then(|s| s.split('-').next()) + .unwrap_or_default() + .parse::()?; + + let beta = configv.next().unwrap_or_default().parse::().ok(); - Ok((major, minor, bugfix)) + Ok((major, minor, bugfix, beta)) } pub fn is_compatible(&self) -> Result { let result = match self.parsed_version()? { - (0, 9, 1) => true, - (0, 9, 0) => true, - (_, _, _) => false, + (0, 10, 0, Some(0)) => true, + (_, _, _, _) => false, }; Ok(result) } pub fn upgrade_notification(&self) -> Result> { - let result = match self.parsed_version()? { - (0, 9, 1) => None, - (0, 9, 0) => Some("App version updated. Improved remap feature"), - (_, _, _) => None, - }; + let result = None; + // let result = match self.parsed_version()? { + // (_, _, _, _) => None, + // }; Ok(result) } @@ -1317,6 +1321,12 @@ impl Config { pub fn modes(&self) -> &ModesConfig { &self.modes } + + /// Set the config's version. + pub fn with_version(mut self, version: String) -> Self { + self.version = version; + self + } } #[cfg(test)] diff --git a/src/config.yml b/src/config.yml index 4458000..eeccf68 100644 --- a/src/config.yml +++ b/src/config.yml @@ -1,4 +1,4 @@ -version: v0.9.1 +version: v0.10.0-beta.0 layouts: custom: {} builtin: