Allow lua to be able to get and modify any field

This commit is contained in:
Christopher Williams 2024-09-16 21:35:11 -04:00
parent fc8dd690c1
commit 49c47ebfd7
4 changed files with 108 additions and 41 deletions

View File

@ -10,8 +10,48 @@ function get(url) end
--- @param xml string
function parse_xml_feed(xml) end
--- @class HtmlParser
HtmlParser = {}
--- @param html string
function HtmlParser:parse(html) end
--- @class Feed
--- @field channel Channel
Feed = {}
--- @return Feed
--- @param xml string
--- @return Feed
function Feed:new(xml) end
--- @return string
function Feed:render() end
--- @class Channel
--- @field title string
--- @field atom_link AtomLink
--- @field articles Article[]
Channel = {}
--- @class Article
--- @field title string
--- @field link string
--- @field creator string
--- @field pub_date string
--- @field categories string[]
--- @field guid Guid
--- @field description string
Article = {}
--- @class AtomLink
--- @field href string
--- @field rel string
--- @field type string
AtomLink = {}
--- @class Guid
--- @field is_perma_link boolean
--- @field guid string
Guid = {}

View File

@ -4,16 +4,18 @@ local feed = rss_parser:new(xml)
local channel = feed.channel
log:info("Title: " .. channel.title)
feed.channel.title = "Really Working Title"
-- local atom_link = AtomLink()
-- atom_link.href = "http://localhost:8081/feed.xml"
-- atom_link.rel = "self"
-- atom_link.mime_type = "application/rss+xml"
--
-- print(feed:render())
-- feed.channel.title = "New Title"
feed.channel.title = "Really Working Title"
feed.channel.atom_link.href = "http://localhost:8081/feed.xml"
feed.channel.articles[1].title = "New Article Title"
feed.channel.articles[2].title = "New Article Title 2"
print(feed.channel.articles[1].description)
feed.channel.articles[1].description = "New Description"
print(feed:render())
-- feed.channel.title = "New Title"
-- print(feed:render())
-- NOTE: HTML Parser
-- local article = get("http://localhost:8081")

View File

@ -1,7 +1,9 @@
use std::{cell::RefCell, rc::Rc};
use log::debug;
use mlua::{
AnyUserDataExt, FromLua, Lua, Result, Table, UserData, UserDataFields, UserDataMethods, Value,
AnyUserDataExt, FromLua, IntoLua, Lua, Result, Table, UserData, UserDataFields,
UserDataMethods, Value,
};
use quick_xml::de::from_str;
use serde::{Deserialize, Serialize};
@ -15,6 +17,12 @@ macro_rules! add_field_method_get {
};
}
macro_rules! add_field_method_get_mut {
($fields:ident, $name:literal, $field:ident) => {
$fields.add_field_method_get($name, |_, this| Ok(this.$field));
};
}
macro_rules! add_field_method_set {
($fields:ident, $name:literal, $field:ident, $type:ty) => {
$fields.add_field_method_set($name, |_, this, value: $type| {
@ -53,6 +61,15 @@ macro_rules! add_field_method_get_set_rc {
};
}
macro_rules! add_field_method_set_rc_option {
($fields:ident, $name:literal, $field:ident, $type:ty) => {
$fields.add_field_method_set($name, |_, this, value: Option<$type>| {
this.$field = value.map(|v| Rc::new(RefCell::new(v)));
Ok(())
});
};
}
macro_rules! implement_from_lua {
($type:ty) => {
impl FromLua<'_> for $type {
@ -66,6 +83,7 @@ macro_rules! implement_from_lua {
};
}
/// Lua object representing an RSS feed as Feed
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
#[serde(rename = "rss")]
pub struct Rss {
@ -73,14 +91,23 @@ pub struct Rss {
channel: Rc<RefCell<Channel>>,
}
type SharedOptionImage = Rc<RefCell<Option<Image>>>;
type SharedRcItem = Rc<RefCell<Item>>;
implement_from_lua!(Rss);
impl UserData for Rss {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
add_field_method_get_set_rc!(fields, "channel", channel, Channel);
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("new", |_, _, xml: String| Ok(Rss::new(&xml)));
methods.add_method("render", |_, this, ()| Ok(this.render()));
}
}
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
pub struct Channel {
title: Option<String>,
#[serde(rename = "atom_link")]
atom_link: Option<AtomLink>,
atom_link: Option<Rc<RefCell<AtomLink>>>,
#[serde(rename = "link")]
link: Option<String>,
description: Option<String>,
@ -92,16 +119,17 @@ pub struct Channel {
#[serde(rename = "sy:updateFrequency")]
update_frequency: Option<String>,
generator: Option<String>,
image: Option<Image>,
image: Option<Rc<RefCell<Image>>>,
#[serde(rename = "item", default)]
items: Vec<Item>,
items: Vec<Rc<RefCell<Item>>>,
}
implement_from_lua!(Channel);
impl UserData for Channel {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
add_field_method_get_set!(fields, "title", title, Option<String>);
add_field_method_get_set!(fields, "atom_link", atom_link, Option<AtomLink>);
add_field_method_get_rc!(fields, "atom_link", atom_link);
add_field_method_set_rc_option!(fields, "atom_link", atom_link, AtomLink);
add_field_method_get_set!(fields, "link", link, Option<String>);
add_field_method_get_set!(fields, "description", description, Option<String>);
add_field_method_get_set!(fields, "last_build_date", last_build_date, Option<String>);
@ -110,9 +138,24 @@ impl UserData for Channel {
add_field_method_get_set!(fields, "update_frequency", update_frequency, Option<String>);
add_field_method_get_set!(fields, "generator", generator, Option<String>);
// TODO: Implement shared reference for image and item
add_field_method_get_set!(fields, "image", image, Option<Image>);
add_field_method_get_set!(fields, "items", items, Vec<Item>);
add_field_method_get_rc!(fields, "image", image);
add_field_method_set_rc_option!(fields, "image", image, Image);
add_field_method_get_rc!(fields, "articles", items);
fields.add_field_method_set("articles", |_, this, items: Vec<Item>| {
this.items = items
.into_iter()
.map(|i| Rc::new(RefCell::new(i)))
.collect();
Ok(())
});
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("new", |_, _, ()| Ok(Channel::default()));
methods.add_method_mut("add_item", |_, this, item: Item| {
this.items.push(Rc::new(RefCell::new(item)));
Ok(())
});
}
}
@ -124,14 +167,7 @@ pub struct AtomLink {
mime_type: Option<String>,
}
impl FromLua<'_> for AtomLink {
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
match value {
Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => unreachable!(),
}
}
}
implement_from_lua!(AtomLink);
impl UserData for AtomLink {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
@ -145,7 +181,6 @@ impl UserData for AtomLink {
}
}
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
struct Image {
url: Option<String>, // Option for missing fields
@ -170,8 +205,9 @@ impl UserData for Image {
}
}
/// Lua object representing an RSS item as Article
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
struct Item {
pub struct Item {
title: Option<String>, // Marked Option
link: Option<String>,
#[serde(rename = "dc:creator")]
@ -235,14 +271,3 @@ impl Rss {
}
}
implement_from_lua!(Rss);
impl UserData for Rss {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
add_field_method_get_set_rc!(fields, "channel", channel, Channel);
}
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("new", |_, _, xml: String| Ok(Rss::new(&xml)));
methods.add_method("render", |_, this, ()| Ok(this.render()));
}
}

View File

@ -1,6 +1,6 @@
use crate::{html_parser::HtmlParser, rss_parser::AtomLink};
use crate::router;
use crate::rss_parser::{Channel, Rss};
use crate::rss_parser::{Channel, Item, Rss};
use log::{debug, error, info, warn};
use mlua::{Lua, MetaMethod, Result, UserData, UserDataMethods};
use std::{
@ -82,9 +82,9 @@ impl Scripting {
add_constructor!("HtmlParser", HtmlParser);
add_constructor!("Feed", Rss);
add_constructor!("Channel", Channel);
add_constructor!("AtomLink", AtomLink);
add_constructor!("Article", Item);
Ok(())
}