import gleam/dict.{type Dict} import gleam/hackney import gleam/http import gleam/http/request import gleam/io import gleam/list import gleam/option.{type Option, None, Some} import gleam/result import gleam/string import gleam/uri import xmlm type RssFeed { RssFeed(title: String, link: String, items: List(RssItem)) } type RssItem { RssItem(title: String, link: String, description: String) } // iterate through top level signals element to form rss feed. // top level is the RssFeed // top level's children are RssItem fn signals_to_rss_feed(signals: #(List(xmlm.Signal), xmlm.Input)) { let _ = io.debug(list.last(signals.0)) let _ = io.debug(signals.1) case xmlm.signal(signals.1) { Ok(#(xmlm.ElementStart(e), input)) -> { io.debug(e) io.debug(input) Nil } _ -> { io.debug("error") Nil } } "" } pub fn main() { let feeds = [ "https://www.reddit.com/.rss", "https://www.reddit.com/r/programming/.rss", ] let _body = list.map(feeds, fn(feed) { let assert Ok(uri) = uri.parse(feed) let assert Ok(request) = request.from_uri(uri) case hackney.send(request) { Ok(response) -> { let _ = io.debug(parse_rss(response.body)) case response.body |> xmlm.from_string |> xmlm.with_stripping(True) |> xmlm.signals { Ok(signals) -> { signals_to_rss_feed(signals) } Error(error) -> { io.debug(error) "" } } "" } Error(error) -> { io.debug(error) "" } } }) } fn parse_rss(xml: String) -> Result(RssFeed, String) { let root = parse_element(xml) case find_element(root, "channel") { Some(channel) -> { let title = find_element_text(channel, "title") |> result.unwrap_both let link = find_element_text(channel, "link") |> result.unwrap_both let items = find_elements(channel, "item") |> list.map(parse_item) |> result.all case items { Ok(items) -> Ok(RssFeed(title, link, items)) Error(e) -> Error(e) } } None -> Error("Channel element not found") } } fn parse_item(item: Element) -> Result(RssItem, String) { let title = find_element_text(item, "title") |> result.unwrap_both let link = find_element_text(item, "link") |> result.unwrap_both let description = find_element_text(item, "description") |> result.unwrap_both Ok(RssItem(title, link, description)) } type Element { Element( name: String, attributes: Dict(String, String), children: List(Element), text: String, ) } fn parse_element(xml: String) -> Element { // Very basic parsing implementation // This won't handle all XML cases, just a simple subset for RSS let name = extract_tag_name(xml) let text = extract_text(xml) let children = extract_children(xml) Element(name, dict.new(), children, text) } fn string_find(s: String, sub: String) -> Int { case string.split(s, "") |> list.index_map(fn(x, i) { #(i, x) }) |> list.filter(fn(a) { a.1 == sub }) |> list.first { Ok(i) -> i.0 _ -> 0 } } fn string_rfind(s: String, sub: String, default) -> Int { case string.split(s, "") |> list.index_map(fn(x, i) { #(i, x) }) |> list.filter(fn(a) { a.1 == sub }) |> list.last { Ok(i) -> i.0 _ -> default } } fn extract_tag_name(xml: String) -> String { // Extract the tag name from the XML string // This is a simple implementation let start = string_find(xml, "<") let end = string_find(xml, ">") string.slice(xml, start + 1, end) } fn extract_text(xml: String) -> String { // Extract the text content from the XML string // This is a simple implementation let start = string_find(xml, ">") let end = string_rfind(xml, "<", string.length(xml)) string.slice(xml, start + 1, end) } fn extract_children(xml: String) -> List(Element) { // Extract child elements from the XML string // This is a simple implementation let children_xml = string.split(xml, " list.filter(fn(part) { string.contains(part, "<") }) |> list.map(parse_element) children_xml } fn find_element(element: Element, tag: String) -> Option(Element) { // element.children // |> list.find(fn(child) { child.name == tag }) case list.find(element.children, fn(child) { child.name == tag }) { Ok(child) -> Some(child) _ -> None } } fn find_elements(element: Element, tag: String) -> List(Element) { element.children |> list.filter(fn(child) { child.name == tag }) } fn find_element_text(element: Element, tag: String) -> Result(String, String) { case find_element(element, tag) { Some(child) -> Ok(child.text) None -> Error("Element not found") } }