From 5b8bc8bac9d24de69e0796b283c34bfbb3cd8330 Mon Sep 17 00:00:00 2001 From: vikingowl Date: Wed, 13 May 2026 04:03:29 +0200 Subject: [PATCH] feat(lua): scaffold mlua-based config module (Phase 3.1) Adds the lua cargo feature and a stubbed crates/owlry/src/lua/ module in preparation for Phase 3 (Lua config layer per docs/lua-api.md). - mlua 0.11 (lua54, vendored, send, serialize) as an optional dep so AUR clean-chroot builds don't depend on system Lua. - New `lua` feature gates the module; included in the `full` feature set so the AUR build ships it. Disabled by default so minimal `cargo install` consumers don't pay for the C compile. - Stub submodules: runtime (LuaContext), api (owlry.* surface), error (LuaConfigError), util (owlry.util.*). Every function is a no-op placeholder for the sub-phases that will wire them. - pub mod lua gated behind #[cfg(feature = "lua")] in lib.rs. cargo check passes with --no-default-features, --features lua, and --features full. 221/221 lib tests still green. Zero clippy warnings from the new module. --- Cargo.lock | 157 ++++++++++++++++++++++++++++++++ crates/owlry/Cargo.toml | 9 ++ crates/owlry/src/lib.rs | 2 + crates/owlry/src/lua/api.rs | 27 ++++++ crates/owlry/src/lua/error.rs | 35 +++++++ crates/owlry/src/lua/mod.rs | 18 ++++ crates/owlry/src/lua/runtime.rs | 45 +++++++++ crates/owlry/src/lua/util.rs | 23 +++++ 8 files changed, 316 insertions(+) create mode 100644 crates/owlry/src/lua/api.rs create mode 100644 crates/owlry/src/lua/error.rs create mode 100644 crates/owlry/src/lua/mod.rs create mode 100644 crates/owlry/src/lua/runtime.rs create mode 100644 crates/owlry/src/lua/util.rs diff --git a/Cargo.lock b/Cargo.lock index 180f66d..8944635 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -482,6 +482,12 @@ dependencies = [ "syn", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "endi" version = "1.1.1" @@ -538,6 +544,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + [[package]] name = "errno" version = "0.3.14" @@ -1582,12 +1599,40 @@ dependencies = [ "winapi", ] +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "lua-src" +version = "550.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e836dc8ae16806c9bdcf42003a88da27d163433e3f9684c52f0301258004a4fb" +dependencies = [ + "cc", +] + +[[package]] +name = "luajit-src" +version = "210.6.6+707c12b" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a86cc925d4053d0526ae7f5bc765dbd0d7a5d1a63d43974f4966cb349ca63295" +dependencies = [ + "cc", + "which", +] + [[package]] name = "mac-notification-sys" version = "0.6.12" @@ -1635,6 +1680,39 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "mlua" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd36acfa49ce6ee56d1307a061dd302c564eee757e6e4cd67eb4f7204846fab" +dependencies = [ + "bstr", + "either", + "erased-serde", + "libc", + "mlua-sys", + "num-traits", + "parking_lot", + "rustc-hash", + "rustversion", + "serde", + "serde-value", +] + +[[package]] +name = "mlua-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1c3a7fc7580227ece249fd90aa2fa3b39eb2b49d3aec5e103b3e85f2c3dfc8" +dependencies = [ + "cc", + "cfg-if", + "libc", + "lua-src", + "luajit-src", + "pkg-config", +] + [[package]] name = "native-tls" version = "0.2.18" @@ -1811,6 +1889,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -1839,6 +1926,7 @@ dependencies = [ "gtk4-layer-shell", "libc", "log", + "mlua", "notify-rust", "reqwest", "serde", @@ -1879,6 +1967,29 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -2010,6 +2121,15 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.6" @@ -2087,6 +2207,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + [[package]] name = "rustc_version" version = "0.4.1" @@ -2133,6 +2259,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "security-framework" version = "3.7.0" @@ -2172,6 +2304,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -2660,6 +2802,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "uds_windows" version = "1.2.1" @@ -2878,6 +3026,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81995fafaaaf6ae47a7d0cc83c67caf92aeb7e5331650ae6ff856f7c0c60c459" +dependencies = [ + "libc", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/crates/owlry/Cargo.toml b/crates/owlry/Cargo.toml index 00c00ad..f826e09 100644 --- a/crates/owlry/Cargo.toml +++ b/crates/owlry/Cargo.toml @@ -63,6 +63,10 @@ reqwest = { version = "0.13", default-features = false, features = ["native-tls" # Async oneshot channel (background thread -> main loop) futures-channel = "0.3" +# Lua 5.4 runtime for user configuration (Phase 3 — opt-in preview in 2.1). +# Vendored C source so AUR clean-chroot builds don't depend on a system Lua. +mlua = { version = "0.11", features = ["lua54", "vendored", "send", "serialize"], optional = true } + [build-dependencies] # GResource compilation for bundled icons glib-build-tools = "0.20" @@ -85,6 +89,10 @@ ssh = [] systemd = [] websearch = [] +# Lua config layer (Phase 3 — opt-in preview in 2.1, default in 2.2, mandatory in 3.0). +# Pulls in mlua's vendored Lua 5.4 runtime. +lua = ["dep:mlua"] + # Bookmarks deferred for 2.0 alongside the widget providers (D20+). # Returns in a later 2.x release with a pure-Rust path that doesn't pull # in libsqlite3-sys for Firefox's places.sqlite. @@ -93,6 +101,7 @@ full = [ "clipboard", "emoji", "filesearch", + "lua", "ssh", "systemd", "websearch", diff --git a/crates/owlry/src/lib.rs b/crates/owlry/src/lib.rs index 81b7171..65ab28e 100644 --- a/crates/owlry/src/lib.rs +++ b/crates/owlry/src/lib.rs @@ -13,6 +13,8 @@ pub mod config; pub mod data; pub mod filter; pub mod ipc; +#[cfg(feature = "lua")] +pub mod lua; pub mod notify; pub mod paths; pub mod providers; diff --git a/crates/owlry/src/lua/api.rs b/crates/owlry/src/lua/api.rs new file mode 100644 index 0000000..39b01a0 --- /dev/null +++ b/crates/owlry/src/lua/api.rs @@ -0,0 +1,27 @@ +//! `owlry.*` Lua API surface. +//! +//! Installs the top-level `owlry` table and its members on the Lua state. +//! Member surface per `docs/lua-api.md` §4–§7: +//! +//! - `owlry.set(table)` — scalar overrides (Phase 3.2) +//! - `owlry.providers(table)` — per-provider enable / config (Phase 3.2) +//! - `owlry.tabs(list)` — tab order + visibility (Phase 3.2) +//! - `owlry.provider(table)` — register a Lua-defined provider (Phase 3.3) +//! - `owlry.theme(table|string)`— theme override (Phase 3.5) +//! - `owlry.profiles(table)` — named profiles (Phase 3.5) +//! - `owlry.util.*` — host helpers (Phase 3.6) +//! +//! Phase 3.1: stub registration entry point only. No tables created yet. + +use mlua::Lua; + +use super::error::LuaConfigError; + +#[allow(dead_code)] +pub(crate) fn install(_lua: &Lua) -> Result<(), LuaConfigError> { + // TODO(phase-3.2): create owlry table, register set / providers / tabs. + // TODO(phase-3.3): register owlry.provider(). + // TODO(phase-3.5): register owlry.theme(), owlry.profiles(). + // TODO(phase-3.6): register owlry.util sub-table. + Ok(()) +} diff --git a/crates/owlry/src/lua/error.rs b/crates/owlry/src/lua/error.rs new file mode 100644 index 0000000..af7efd1 --- /dev/null +++ b/crates/owlry/src/lua/error.rs @@ -0,0 +1,35 @@ +//! Error type for Lua configuration loading and evaluation. +//! +//! Distinct from [`crate::config::ConfigError`] so that the daemon can report +//! "your Lua config failed" separately from "your TOML config failed" and +//! pinpoint the offending file path. + +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum LuaConfigError { + #[error("failed to read Lua config at {path}: {source}")] + Read { + path: PathBuf, + #[source] + source: std::io::Error, + }, + + #[error("Lua evaluation error in {path}: {source}")] + Eval { + path: PathBuf, + #[source] + source: mlua::Error, + }, + + #[error("invalid value for `{key}` in {path}: {message}")] + InvalidValue { + path: PathBuf, + key: String, + message: String, + }, + + #[error("Lua runtime error: {0}")] + Runtime(#[from] mlua::Error), +} diff --git a/crates/owlry/src/lua/mod.rs b/crates/owlry/src/lua/mod.rs new file mode 100644 index 0000000..c20b974 --- /dev/null +++ b/crates/owlry/src/lua/mod.rs @@ -0,0 +1,18 @@ +//! Lua configuration layer (Phase 3). +//! +//! The user configuration surface lives at `~/.config/owlry/owlry.lua` (D23). +//! In 2.1 this is an opt-in preview behind the `lua` cargo feature; the +//! existing TOML loader in [`crate::config`] remains the source of truth +//! when no `owlry.lua` is present. See `docs/lua-api.md` for the full +//! design (D1–D24) and roadmap to 3.0. +//! +//! Phase 3.1 only scaffolds the module — every submodule is a stub with no +//! call sites yet. Wiring happens in 3.2+. + +pub mod api; +pub mod error; +pub mod runtime; +pub mod util; + +pub use error::LuaConfigError; +pub use runtime::LuaContext; diff --git a/crates/owlry/src/lua/runtime.rs b/crates/owlry/src/lua/runtime.rs new file mode 100644 index 0000000..20f61f9 --- /dev/null +++ b/crates/owlry/src/lua/runtime.rs @@ -0,0 +1,45 @@ +//! Lua runtime wrapper. +//! +//! [`LuaContext`] owns a single `mlua::Lua` state and the `owlry.*` API +//! surface installed on it. Phase 3.1 only stubs the type — actual table +//! installation and evaluation land in 3.2+. + +use std::path::Path; + +use mlua::Lua; + +use super::error::LuaConfigError; + +#[allow(dead_code)] +pub struct LuaContext { + lua: Lua, +} + +#[allow(dead_code)] +impl LuaContext { + /// Build a fresh Lua state with the `owlry.*` API surface installed. + /// + /// Stub: returns a bare `Lua` state. Phase 3.2 wires `owlry.set`, + /// `owlry.providers`, `owlry.tabs`; later sub-phases add `provider`, + /// `theme`, `profiles`, and `util`. + pub fn new() -> Result { + let lua = Lua::new(); + Ok(Self { lua }) + } + + /// Evaluate the user's `owlry.lua` file against this context. + /// + /// Stub: not wired in 3.1. Phase 3.4 hooks this into the config + /// resolution path (`owlry.lua` > `config.toml` > defaults). + pub fn eval_file(&self, _path: &Path) -> Result<(), LuaConfigError> { + // TODO(phase-3.2): load file via std::fs, evaluate with self.lua, + // capture and convert mlua::Error into Eval { path, .. }. + Ok(()) + } + + /// Expose the underlying Lua state for sub-phases that need to register + /// host functions or read collected state back out. + pub(crate) fn raw(&self) -> &Lua { + &self.lua + } +} diff --git a/crates/owlry/src/lua/util.rs b/crates/owlry/src/lua/util.rs new file mode 100644 index 0000000..699ce2e --- /dev/null +++ b/crates/owlry/src/lua/util.rs @@ -0,0 +1,23 @@ +//! `owlry.util.*` host helpers exposed to user Lua configs. +//! +//! Surface per `docs/lua-api.md` §7: +//! +//! - `owlry.util.shell(cmd) -> string` — capture stdout (trimmed) +//! - `owlry.util.shell_lines(cmd) -> {string}` +//! - `owlry.util.read_file(path) -> string` +//! - `owlry.util.glob(pattern) -> {string}` +//! - `owlry.util.env(name) -> string|nil` +//! - `owlry.util.hostname() -> string` +//! +//! Phase 3.1: stub module. Implementations land in 3.6. + +use mlua::Lua; + +use super::error::LuaConfigError; + +#[allow(dead_code)] +pub(crate) fn install(_lua: &Lua) -> Result<(), LuaConfigError> { + // TODO(phase-3.6): build owlry.util table with shell / shell_lines / + // read_file / glob / env / hostname helpers. + Ok(()) +}