Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778use crate::dom::{LuaNode, parse_html};use crate::rss::FeedItem;use mlua::{Lua, Table};
#[derive(Debug)]pub enum RunError { Lua(mlua::Error), MissingField(&'static str),}
impl std::fmt::Display for RunError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { RunError::Lua(e) => write!(f, "method script failed: {e}"), RunError::MissingField(field) => write!( f, "an item returned by the method script is missing required field `{field}`" ), } }}
impl std::error::Error for RunError {}
/// Runs a method script against fetched HTML. A fresh `Lua` VM and DOM tree/// are created per call: `mlua::Lua` and `rcdom::Handle` are not `Send`/`Sync`,/// and the scheduler dispatches one fetch per worker thread, so nothing here/// is meant to outlive the thread that calls this function.////// The script is called with `(root, url)`: the parsed DOM and the site's/// own fetch URL, the latter needed to resolve relative links a page emits/// (`href="/foo"`) into absolute ones. A script that doesn't need it can/// still write `local root = ...` — Lua drops the extra value silently. A/// `fetch(url) -> {status=, body=}` global is also available, for a script/// that needs a second page (e.g. an article's own page for its full body),/// along with `parse_html(html) -> node` to walk whatever it fetches.pub fn run_method(source: &str, url: &str, html: &str) -> Result<Vec<FeedItem>, RunError> { let lua = Lua::new(); crate::http_fetch::register(&lua).map_err(RunError::Lua)?; crate::dom::register(&lua).map_err(RunError::Lua)?; let dom = parse_html(html); let root = LuaNode::root(&dom);
let items: Table = lua.load(source).call((root, url)).map_err(RunError::Lua)?;
let mut out = Vec::new(); for entry in items.sequence_values::<Table>() { let item = entry.map_err(RunError::Lua)?; let title = item .get::<Option<String>>("title") .map_err(RunError::Lua)? .ok_or(RunError::MissingField("title"))?; let link = item .get::<Option<String>>("link") .map_err(RunError::Lua)? .ok_or(RunError::MissingField("link"))?; let description = item .get::<Option<String>>("description") .map_err(RunError::Lua)?; let pub_date = item .get::<Option<String>>("pub_date") .map_err(RunError::Lua)?; let guid = item.get::<Option<String>>("guid").map_err(RunError::Lua)?; let author = item .get::<Option<String>>("author") .map_err(RunError::Lua)?; out.push(FeedItem { title, link, description, pub_date, guid, author, }); } Ok(out)}