From 79999cecedd84e99655fbcdba0bd4e715a22173f Mon Sep 17 00:00:00 2001 From: alyx Date: Fri, 5 Apr 2024 17:27:46 -0400 Subject: Lua support feature-complete Full implementation of debug mode still pending --- src/main.rs | 2 +- src/theming.rs | 6 ++++-- src/theming/hbs.rs | 4 ++-- src/theming/lua-lib/expect.lua | 6 +++--- src/theming/lua-lib/html.lua | 14 ++++++++++---- src/theming/lua.rs | 39 +++++++++++++++++++++++++++------------ 6 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/main.rs b/src/main.rs index afd1347..57975db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,7 +52,7 @@ async fn main() { let (userinfo, refresh) = get_userinfo(&s).await; let (ctx, status) = get_ctx(userinfo, q.font, q.rest).await; - let (theme, res) = render_theme(q.theme.as_deref(), &ctx); + let (theme, res) = tokio::task::spawn_blocking(move || render_theme(q.theme.as_deref(), &ctx)).await.expect("fatal error in theme rendering"); log::debug!(target: "lfm_embed::main::user", "Using theme {theme}"); match res { diff --git a/src/theming.rs b/src/theming.rs index d8525f9..1d7b1f2 100644 --- a/src/theming.rs +++ b/src/theming.rs @@ -12,10 +12,12 @@ pub fn render_theme(name: Option<&str>, ctx: &crate::ctx::Ctx) -> (String, Resul if let Some(name) = name { theme = name; - res = hbs::render_theme(name, ctx) + res = lua::render_theme(name, ctx) + .or_else(|| hbs::render_theme(name, ctx)) } - res = res.or_else(|| { log::debug!("Falling back to default theme!"); theme = &CONFIG.default_theme; None }) + res = res.or_else(|| { log::debug!("Falling back to default theme from requested theme `{theme}`!"); theme = &CONFIG.default_theme; None }) + .or_else(|| lua::render_theme(theme, ctx)) .or_else(|| hbs::render_theme(theme, ctx)); let res = res.unwrap_or_else(|| { log::error!("Couldn't load requested theme or default theme `{}`!", CONFIG.default_theme); Err(StatusCode::INTERNAL_SERVER_ERROR)}); diff --git a/src/theming/hbs.rs b/src/theming/hbs.rs index e5da0cd..67a2ae4 100644 --- a/src/theming/hbs.rs +++ b/src/theming/hbs.rs @@ -15,13 +15,13 @@ static HANDLEBARS: LazyLock = LazyLock::new(|| { hb.register_helper("url-encode", Box::new(url_encode)); for (key, fulltext) in INTERNAL_THEMES { - log::info!("Registering internal theme `{key}`"); + log::info!("Registering internal handlebars theme `{key}`"); hb.register_template_string(key, fulltext).unwrap(); } hb.set_dev_mode(CONFIG.theme_debug); if let Some(themes_dir) = CONFIG.theme_dir.as_ref() { - log::info!("Registering theme dir `{themes_dir}`"); + log::info!("Registering handlebars theme dir `{themes_dir}` with extension `{}`.", CONFIG.theme_ext_hbs); hb.register_templates_directory(&CONFIG.theme_ext_hbs, themes_dir.as_ref()).unwrap(); } diff --git a/src/theming/lua-lib/expect.lua b/src/theming/lua-lib/expect.lua index 9afbb49..20772b2 100644 --- a/src/theming/lua-lib/expect.lua +++ b/src/theming/lua-lib/expect.lua @@ -50,7 +50,7 @@ local function get_display_type(value, t) -- Cobalt and only read the metatable for tables/userdata. if t ~= "table" and t ~= "userdata" then return t end - local metatable = debug.getmetatable(value) + local metatable = getmetatable(value) if not metatable then return t end local name = rawget(metatable, "__name") @@ -72,8 +72,8 @@ local function expect(index, value, ...) -- If we can determine the function name with a high level of confidence, try to include it. local name - local ok, info = pcall(debug.getinfo, 3, "nS") - if ok and info.name and info.name ~= "" and info.what ~= "C" then name = info.name end + -- local ok, info = pcall(debug.getinfo, 3, "nS") + -- if ok and info.name and info.name ~= "" and info.what ~= "C" then name = info.name end t = get_display_type(value, t) diff --git a/src/theming/lua-lib/html.lua b/src/theming/lua-lib/html.lua index 386618b..6786f96 100644 --- a/src/theming/lua-lib/html.lua +++ b/src/theming/lua-lib/html.lua @@ -16,8 +16,8 @@ local function html(el, tbl) expect(1, el, "string") expect(2, tbl, "table") - innerHtml = "" - attributes = "" + local innerHtml = "" + local attributes = "" for k, v in pairs(tbl) do if type(k) == "string" then @@ -39,6 +39,12 @@ local function root(tbl) end return setmetatable( - { root = root }, + {}, { __call = html, - __index = function (idx) return function(tbl) html(idx, tbl) end end }) + __index = function (_, idx) + if idx == "root" then + return root + else + return function(tbl) return html(idx, tbl) end + end + end }) diff --git a/src/theming/lua.rs b/src/theming/lua.rs index c9168cb..6bd6989 100644 --- a/src/theming/lua.rs +++ b/src/theming/lua.rs @@ -1,10 +1,10 @@ // SPDX-License-Identifier: AGPL-3.0-only use std::sync::LazyLock; -use std::ops::Deref; use std::path::Path; +use std::fs; -use tokio::sync::Mutex; -use mlua::{Lua, Compiler, StdLib, Value, LuaOptions}; +use std::sync::Mutex; +use mlua::{Lua, LuaSerdeExt, Compiler, StdLib, Value, LuaOptions, Table}; use http::StatusCode; use crate::CONFIG; @@ -37,16 +37,19 @@ static LUA: LazyLock> = LazyLock::new(|| { let themes = lua.create_table().expect("creating themes table"); for (k, v) in INTERNAL_THEMES { + log::info!("Registering compiled lua theme `{k}`."); let _ = themes.set(*k, lua.load(*v).into_function().expect("loading internal theme")); } if let Some(theme_dir) = CONFIG.theme_dir.as_ref() { + log::info!("Registering lua theme dir `{theme_dir}` with extension `{}`.", CONFIG.theme_ext_lua); for (k, v) in walk_dir(theme_dir.as_ref().as_ref()).expect("walking theme dir") { - let _ = themes.set(k, lua.load(v).into_function().expect("loading internal theme")); + log::info!("Registering runtime lua theme `{k}`."); + let _ = themes.set(k, lua.load(v).eval::().expect("loading internal theme")); } } - let _ = lua.globals().set("__themes", themes).unwrap(); + lua.globals().set("__themes", themes).unwrap(); lua.globals().set_readonly(true); @@ -54,7 +57,7 @@ static LUA: LazyLock> = LazyLock::new(|| { }); fn walk_dir(path: &Path) -> std::io::Result> { - use std::fs; + let ext = CONFIG.theme_ext_lua.as_ref(); let mut path_bits = vec![]; let mut dir_readers = vec![fs::read_dir(path)?]; @@ -67,7 +70,7 @@ fn walk_dir(path: &Path) -> std::io::Result> { let name = ent.file_name().into_string().expect("why do you have such FUCKED UP FILE PATHS"); let ty = ent.file_type()?; if ty.is_file() && name.ends_with(CONFIG.theme_ext_lua.as_ref()) { - ret.push((path_bits.join("") + &name, fs::read_to_string(ent.path())?)); + ret.push((path_bits.join("") + &name[..name.len() - ext.len()], fs::read_to_string(ent.path())?)); } else if ty.is_dir() { path_bits.push(name); @@ -82,10 +85,22 @@ fn walk_dir(path: &Path) -> std::io::Result> { Ok(ret) } -pub fn touch() { - let _ = LUA.deref(); -} - pub fn render_theme(name: &str, ctx: &crate::ctx::Ctx) -> Option> { - return None + LUA.clear_poison(); + let lua = LUA.lock().expect("FIXME: Mutex poisoning race condition."); + let themes: Table = lua.globals().get("__themes").unwrap(); + let Ok(Value::Function(theme)) = themes.get(name) + else { + return None; + }; + + let ctx = match lua.to_value(ctx) { + Ok(ok) => ok, + Err(e) => { + log::error!("Lua context serialization error: {e}"); + return Some(Err(StatusCode::INTERNAL_SERVER_ERROR)) + } + }; + + Some(theme.call((ctx,)).map_err(|e| { log::error!("Lua theme execution error: {e}"); StatusCode::INTERNAL_SERVER_ERROR })) } -- cgit v1.2.3-70-g09d2