summaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/src')
-rw-r--r--server/src/server.gleam200
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")
+ }
+}