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.
This commit is contained in:
2026-05-13 04:03:29 +02:00
parent d9cde0b3a4
commit 5b8bc8bac9
8 changed files with 316 additions and 0 deletions
Generated
+157
View File
@@ -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"
+9
View File
@@ -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",
+2
View File
@@ -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;
+27
View File
@@ -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(())
}
+35
View File
@@ -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),
}
+18
View File
@@ -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 (D1D24) 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;
+45
View File
@@ -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<Self, LuaConfigError> {
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
}
}
+23
View File
@@ -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(())
}