use std::collections::HashMap; use std::fmt; use futures::future::join_all; use rss::{GuidBuilder, Item, ItemBuilder}; use serde::Deserialize; use time::format_description::well_known::iso8601::FormattedComponents; use time::format_description::well_known::{iso8601, Iso8601}; use time::Date; use crate::scrapers::page_url; use crate::{hash, scrapers}; pub mod route; pub mod template; #[derive(Clone, Debug, Deserialize)] pub struct Feed { pub title: String, pub url: String, pub id: String, } impl fmt::Display for Feed { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} ({})", self.title, self.url) } } #[derive(Clone, Debug, Deserialize)] pub struct Footprint { pub title: String, pub text: String, pub url: String, pub date: Date, pub page: u8, } const ISO8601_DATE: u128 = iso8601::Config::DEFAULT .set_formatted_components(FormattedComponents::Date) .encode(); impl Footprint { pub fn into_rss_item(self, root_url: &str) -> Item { let desc = format!( "{}

---

from {}", self.text, page_url(root_url, self.page) ); ItemBuilder::default() .title(Some(self.title)) .pub_date(self.date.format(&Iso8601::).ok()) .link(Some(self.url.clone())) .description(Some(desc)) .guid(Some(GuidBuilder::default().value(self.url).build())) .build() } } pub async fn enrich_feeds(feed_urls: Vec) -> HashMap { let feeds = join_all(feed_urls.iter().map(|feed_url| async { let title = scrapers::feed_title(feed_url).await.unwrap(); let id = hash::fnv_str(feed_url); Feed { title, url: feed_url.clone(), id, } })) .await; feeds.iter().map(|x| (x.id.clone(), x.clone())).collect() }