// rss - RSS 2.0 feed generator for Lux // // Generates valid RSS 2.0 XML feeds from structured data. import xml // An RSS feed item type Item = | Item(String, String, String, String, String) // Item(title, link, description, pubDate, guid) // An RSS channel/feed type Feed = | Feed(String, String, String, String, List) // Feed(title, link, description, language, items) // Create a feed item pub fn item(title: String, link: String, description: String, pubDate: String): Item = Item(title, link, description, pubDate, link) // Create a feed item with custom GUID pub fn itemWithGuid(title: String, link: String, description: String, pubDate: String, guid: String): Item = Item(title, link, description, pubDate, guid) // Create an RSS feed pub fn feed(title: String, link: String, description: String, items: List): Feed = Feed(title, link, description, "en", items) // Create an RSS feed with language pub fn feedWithLang(title: String, link: String, description: String, language: String, items: List): Feed = Feed(title, link, description, language, items) // Access item fields pub fn itemTitle(i: Item): String = match i { Item(t, _, _, _, _) => t, } pub fn itemLink(i: Item): String = match i { Item(_, l, _, _, _) => l, } pub fn itemDescription(i: Item): String = match i { Item(_, _, d, _, _) => d, } pub fn itemPubDate(i: Item): String = match i { Item(_, _, _, p, _) => p, } pub fn itemGuid(i: Item): String = match i { Item(_, _, _, _, g) => g, } // Convert ISO date (2025-01-29) to RFC 822 format (29 Jan 2025 00:00:00 GMT) pub fn isoToRfc822(isoDate: String): String = if String.length(isoDate) < 10 then isoDate else { let year = String.substring(isoDate, 0, 4) let month = String.substring(isoDate, 5, 7) let day = String.substring(isoDate, 8, 10) let monthName = if month == "01" then "Jan" else if month == "02" then "Feb" else if month == "03" then "Mar" else if month == "04" then "Apr" else if month == "05" then "May" else if month == "06" then "Jun" else if month == "07" then "Jul" else if month == "08" then "Aug" else if month == "09" then "Sep" else if month == "10" then "Oct" else if month == "11" then "Nov" else "Dec" day + " " + monthName + " " + year + " 00:00:00 GMT" } // Render a single item to XML string fn renderItemXml(i: Item): String = match i { Item(title, link, desc, pubDate, guid) => xml.render(xml.el("item", [ xml.textEl("title", title), xml.textEl("link", link), xml.textEl("description", desc), xml.textEl("pubDate", isoToRfc822(pubDate)), xml.textElAttr("guid", [xml.attr("isPermaLink", "true")], guid) ])), } // Render items to concatenated XML string fn renderItemsXml(items: List): String = match List.head(items) { None => "", Some(i) => renderItemXml(i) + match List.tail(items) { Some(rest) => renderItemsXml(rest), None => "", }, } // Render an RSS feed to XML string pub fn render(f: Feed): String = match f { Feed(title, link, desc, lang, items) => { let itemsXml = renderItemsXml(items) "\n" + "" + "" + "" + xml.escape(title) + "" + "" + xml.escape(link) + "" + "" + xml.escape(desc) + "" + "" + lang + "" + "Lux RSS Package" + itemsXml + "" }, }