use crate::Result; use reqwest::Url; use chrono::{DateTime, Utc}; use std::hash::{Hash, Hasher}; pub struct FetchedRSSItem { guid: String, title: String, description: String, content: String, } impl FetchedRSSItem { pub fn guid(&self) -> &str { &self.guid } pub fn title(&self) -> &str { &self.title } pub fn description(&self) -> &str { &self.description } pub fn content(&self) -> &str { &self.content } fn parse(item: rss::Item) -> Self { FetchedRSSItem { guid: Self::get_or_create_guid(&item), title: item.title().unwrap_or("").to_string(), description: item.description().unwrap_or("").to_string(), content: item.content().unwrap_or("").to_string(), } } fn get_or_create_guid(item: &rss::Item) -> String { if let Some(guid) = item.guid() { return guid.value().to_string(); } let mut hasher = std::collections::hash_map::DefaultHasher::new(); item.link().unwrap_or("").hash(&mut hasher); item.title().unwrap_or("").hash(&mut hasher); item.description().unwrap_or("").hash(&mut hasher); format!("gen-{:x}", hasher.finish()) } } pub struct FetchedRSSChannel { title: String, link: Url, description: String, items: Vec, fetched_at: DateTime, } impl FetchedRSSChannel { pub fn title(&self) -> &str { &self.title } pub fn link(&self) -> &Url { &self.link } pub fn description(&self) -> &str { &self.description } pub fn items(&self) -> &[FetchedRSSItem] { &self.items } pub fn fetched_at(&self) -> &DateTime { &self.fetched_at } pub fn parse(rss: rss::Channel) -> Result { Ok(FetchedRSSChannel { title: rss.title, link: Url::parse(&rss.link)?, description: rss.description, items: rss.items.into_iter().map(FetchedRSSItem::parse).collect(), fetched_at: Utc::now(), }) } }