diff options
| author | Fuwn <[email protected]> | 2024-07-27 19:03:49 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-07-27 19:03:49 -0700 |
| commit | efeff94b550651ab8f134e705b07b80c1c8faeba (patch) | |
| tree | d345d0a0d5602cc01ee164a02a08b07bf97ecfd8 /server/src | |
| download | yuna-main.tar.xz yuna-main.zip | |
Diffstat (limited to 'server/src')
| -rw-r--r-- | server/src/server.gleam | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/server/src/server.gleam b/server/src/server.gleam new file mode 100644 index 0000000..2ea7b3e --- /dev/null +++ b/server/src/server.gleam @@ -0,0 +1,200 @@ +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") + } +} |