summaryrefslogtreecommitdiff
path: root/src/manager.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/manager.rs')
-rw-r--r--src/manager.rs107
1 files changed, 107 insertions, 0 deletions
diff --git a/src/manager.rs b/src/manager.rs
new file mode 100644
index 0000000..839c8dc
--- /dev/null
+++ b/src/manager.rs
@@ -0,0 +1,107 @@
1use crate::{
2 error::{AddError, Error, IOError, PullError},
3 feed::{Entry, Feed},
4 status::{PullStatus, StoreStatus},
5};
6
7use chrono::prelude::*;
8use url::Url;
9
10#[derive(Default)]
11pub struct Manager {
12 feeds: Vec<Feed>,
13}
14
15impl Manager {
16 pub async fn add_feed(&mut self, url: &str) -> Result<PullStatus, Error> {
17 let link = Url::parse(&url).map_err(|e| AddError::InvalidUrl(e.to_string()))?;
18
19 // check if this feed is already present
20 if self.feeds.iter().any(|f| f.link == link) {
21 return Err(AddError::DuplicateLink.into());
22 }
23
24 // construct a new feed
25 let mut feed = Feed::new(link.clone());
26
27 let status = feed
28 .pull()
29 .await
30 .map_err(|pull_err| Error::Pull(link, pull_err))?;
31
32 // add new feed
33 self.feeds.push(feed);
34
35 Ok(status)
36 }
37
38 pub async fn pull(&mut self) -> Vec<Result<PullStatus, PullError>> {
39 futures::future::join_all(self.feeds.iter_mut().map(Feed::pull)).await
40 }
41
42 pub fn list_entries(&self) -> impl Iterator<Item = &Entry> {
43 EntryIterator {
44 all_entries: self.feeds.iter().map(Feed::entries).collect(),
45 }
46 }
47
48 pub fn list_feeds(&self) -> impl Iterator<Item = &Feed> {
49 self.feeds.iter()
50 }
51
52 pub async fn store(&self) -> Result<StoreStatus, IOError> {
53 let path = crate::dirs::store_path().ok_or(IOError::MissingStorePath)?;
54 let content = serde_yaml::to_string(&self.feeds)?;
55 std::fs::write(path, content)?;
56
57 Ok(StoreStatus::new(self.feeds.len()))
58 }
59
60 pub async fn load() -> Result<Self, IOError> {
61 let path = crate::dirs::store_path().ok_or(IOError::MissingStorePath)?;
62 let content = std::fs::read_to_string(path)?;
63 let feeds = serde_yaml::from_str(&content)?;
64
65 Ok(Self { feeds })
66 }
67}
68
69struct EntryIterator<'e> {
70 all_entries: Vec<&'e [Entry]>,
71}
72
73impl<'e> Iterator for EntryIterator<'e> {
74 type Item = &'e Entry;
75
76 fn next(&mut self) -> Option<Self::Item> {
77 let mut min_index = None;
78 let mut last_date = DateTime::<Utc>::MIN_UTC;
79 for (idx, latest_entry) in self
80 .all_entries
81 .iter()
82 .map(|entries| entries.first())
83 .enumerate()
84 {
85 if let Some(entry) = latest_entry {
86 if last_date < entry.published {
87 last_date = entry.published;
88 min_index = Some(idx);
89 }
90 }
91 }
92
93 match min_index {
94 Some(idx) => {
95 let entries = self.all_entries.get_mut(idx).unwrap();
96 let e = &entries[0];
97 if entries.len() > 1 {
98 *entries = &entries[1..];
99 } else {
100 self.all_entries.remove(idx);
101 }
102 Some(e)
103 }
104 None => None,
105 }
106 }
107}