use crate::{ error::{AddError, Error, IOError, PullError}, feed::{Entry, Feed}, status::{PullStatus, StoreStatus}, }; use chrono::prelude::*; use url::Url; #[derive(Default)] pub struct Manager { feeds: Vec, } impl Manager { pub async fn add_feed(&mut self, url: &str) -> Result { let link = Url::parse(&url).map_err(|e| AddError::InvalidUrl(e.to_string()))?; // check if this feed is already present if self.feeds.iter().any(|f| f.link == link) { return Err(AddError::DuplicateLink.into()); } // construct a new feed let mut feed = Feed::new(link.clone()); let status = feed .pull() .await .map_err(|pull_err| Error::Pull(link, pull_err))?; // add new feed self.feeds.push(feed); Ok(status) } pub async fn pull(&mut self) -> Vec> { futures::future::join_all(self.feeds.iter_mut().map(Feed::pull)).await } pub fn list_entries(&self) -> impl Iterator { EntryIterator { all_entries: self.feeds.iter().map(Feed::entries).collect(), } } pub fn list_feeds(&self) -> impl Iterator { self.feeds.iter() } pub fn store(&self) -> Result { let path = crate::dirs::store_path().ok_or(IOError::MissingStorePath)?; let content = serde_yaml::to_string(&self.feeds)?; std::fs::write(&path, content)?; let count = self.feeds.len(); let location = path; Ok(StoreStatus::new(count, location)) } pub fn load() -> Result { let path = crate::dirs::store_path().ok_or(IOError::MissingStorePath)?; let content = std::fs::read_to_string(path)?; let feeds = serde_yaml::from_str(&content)?; Ok(Self { feeds }) } } struct EntryIterator<'e> { all_entries: Vec<&'e [Entry]>, } impl<'e> Iterator for EntryIterator<'e> { type Item = &'e Entry; fn next(&mut self) -> Option { let mut min_index = None; let mut last_date = DateTime::::MIN_UTC; for (idx, latest_entry) in self .all_entries .iter() .map(|entries| entries.first()) .enumerate() { if let Some(entry) = latest_entry { if last_date < entry.published { last_date = entry.published; min_index = Some(idx); } } } match min_index { Some(idx) => { let entries = self.all_entries.get_mut(idx).unwrap(); let e = &entries[0]; if entries.len() > 1 { *entries = &entries[1..]; } else { self.all_entries.remove(idx); } Some(e) } None => None, } } }