From d6a294c04305cf065cecad379bdd03986376b27d Mon Sep 17 00:00:00 2001 From: gyoder <70408179+gyoder@users.noreply.github.com> Date: Sat, 21 Jun 2025 23:29:00 -0600 Subject: started htmlua-parser --- htmlua-parser/src/lib.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 htmlua-parser/src/lib.rs (limited to 'htmlua-parser/src') diff --git a/htmlua-parser/src/lib.rs b/htmlua-parser/src/lib.rs new file mode 100644 index 0000000..80a21dd --- /dev/null +++ b/htmlua-parser/src/lib.rs @@ -0,0 +1,109 @@ +#![warn(clippy::pedantic)] + +use std::cell::RefCell; +use std::fmt::Write; +use std::rc::Rc; + +use anyhow::Result; +use anyhow::anyhow; +use kuchikiki::{ + NodeRef, + traits::TendrilSink, +}; +use mlua::{Lua, Table}; +use tendril::fmt; +use tendril::{Atomicity, Tendril}; + +fn create_luahtml_stdlib(l: &Lua, stdout: &Rc>) -> mlua::Result { + let t = l.create_table()?; + + // This cannot be the best way to do this + + let stdout_println = stdout.clone(); + t.set( + "println", + l.create_function(move |_, text: String| { + let mut stdout_ref = stdout_println.borrow_mut(); + writeln!(stdout_ref, "{text}").map_err(mlua::Error::external)?; + Ok(()) + })?, + )?; + + let stdout_print = stdout.clone(); + t.set( + "print", + l.create_function(move |_, text: String| { + let mut stdout_ref = stdout_print.borrow_mut(); + write!(stdout_ref, "{text}").map_err(mlua::Error::external)?; + Ok(()) + })?, + )?; + Ok(t) +} + +pub fn parse>>(to_parse: T) -> Result +where + tendril::Tendril: std::convert::From>, +{ + let document = kuchikiki::parse_html().one(to_parse.into()); + let lua = Lua::new(); + let globals = lua.globals(); + + let stdout: Rc> = Rc::new(RefCell::new(String::new())); + + let htmlua_table = create_luahtml_stdlib(&lua, &stdout) + .map_err(|e| anyhow::anyhow!("Failed to create Lua stdlib: {}", e))?; + + globals + .set("htmlua", htmlua_table) + .map_err(|e| anyhow::anyhow!("Failed to set global: {}", e))?; + + let lua_elements: Vec<_> = match document.select("lua") { + Ok(e) => e.collect(), + Err(()) => return Err(anyhow!("Unable to find Lua")), + }; + + for node in lua_elements { + if let Some(text_node) = node.as_node().first_child() { + if let Some(lua_code) = text_node.as_text() { + stdout.borrow_mut().clear(); + lua.load(lua_code.borrow().as_str()) + .exec() + .map_err(|e| anyhow::anyhow!("Failed to execute Lua: {}", e))?; + node.as_node() + .insert_before(NodeRef::new_text(stdout.borrow().as_str())); + node.as_node().detach(); + } + } + } + + Ok(document) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn base() -> Result<()> { + let page = r#" + + + + Basic HTML Page + + +

Hello World

+

This is a paragraph.

+ + htmlua.println("Test from Lua!") + + + "#; + let d = parse(page)?; + let text = d.select_first("span").unwrap().as_node().text_contents(); + assert_eq!(text, "Test from Lua!\n"); + assert!(d.select_first("lua").is_err()); + Ok(()) + } +} -- cgit v1.3.1