diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/error.rs | 2 | ||||
-rw-r--r-- | src/feed.rs | 3 | ||||
-rw-r--r-- | src/lib.rs | 17 | ||||
-rw-r--r-- | src/main.rs | 138 | ||||
-rw-r--r-- | src/manager.rs | 11 | ||||
-rw-r--r-- | src/status.rs | 35 |
6 files changed, 162 insertions, 44 deletions
diff --git a/src/error.rs b/src/error.rs index 8a2059e..2638fc2 100644 --- a/src/error.rs +++ b/src/error.rs | |||
@@ -10,7 +10,7 @@ pub enum Error { | |||
10 | #[error("error parsing entry: {0}")] | 10 | #[error("error parsing entry: {0}")] |
11 | Entry(#[from] EntryError), | 11 | Entry(#[from] EntryError), |
12 | 12 | ||
13 | #[error("error adding field: {0}")] | 13 | #[error("error adding feed: {0}")] |
14 | Add(#[from] AddError), | 14 | Add(#[from] AddError), |
15 | } | 15 | } |
16 | 16 | ||
diff --git a/src/feed.rs b/src/feed.rs index 3926fd4..2875667 100644 --- a/src/feed.rs +++ b/src/feed.rs | |||
@@ -87,10 +87,11 @@ impl Feed { | |||
87 | .partition(Result::is_ok); | 87 | .partition(Result::is_ok); |
88 | 88 | ||
89 | // pull status | 89 | // pull status |
90 | let title = self.title.clone(); | ||
90 | let count = entries.len().saturating_sub(self.total_count()); | 91 | let count = entries.len().saturating_sub(self.total_count()); |
91 | let errors = errors.into_iter().map(Result::unwrap_err).collect(); | 92 | let errors = errors.into_iter().map(Result::unwrap_err).collect(); |
92 | 93 | ||
93 | let pull_status = PullStatus::new(count, errors); | 94 | let pull_status = PullStatus::new(title, count, errors); |
94 | 95 | ||
95 | // update entries | 96 | // update entries |
96 | self.entries = entries.into_iter().map(Result::unwrap).collect(); | 97 | self.entries = entries.into_iter().map(Result::unwrap).collect(); |
@@ -3,3 +3,20 @@ pub mod error; | |||
3 | pub mod feed; | 3 | pub mod feed; |
4 | pub mod manager; | 4 | pub mod manager; |
5 | pub mod status; | 5 | pub mod status; |
6 | |||
7 | pub trait PrintResult { | ||
8 | fn print(&self); | ||
9 | } | ||
10 | |||
11 | impl<T, E> PrintResult for Result<T, E> | ||
12 | where | ||
13 | T: std::fmt::Display, | ||
14 | E: std::fmt::Display, | ||
15 | { | ||
16 | fn print(&self) { | ||
17 | match self { | ||
18 | Ok(ok) => println!("{ok}"), | ||
19 | Err(err) => eprintln!("{err}"), | ||
20 | } | ||
21 | } | ||
22 | } | ||
diff --git a/src/main.rs b/src/main.rs index fccc7fd..65de274 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -1,37 +1,117 @@ | |||
1 | use syn::manager::Manager; | 1 | use clap::{Args, Parser, Subcommand, ValueEnum}; |
2 | use syn::{manager::Manager, PrintResult}; | ||
2 | 3 | ||
3 | #[tokio::main(flavor = "current_thread")] | 4 | // #[tokio::main(flavor = "current_thread")] |
4 | async fn main() { | 5 | // async fn main() { |
5 | let mut manager = Manager::default(); | 6 | // let mut manager = Manager::load().unwrap(); |
6 | 7 | // | |
7 | let feeds = vec![ | 8 | // let feeds = vec![ |
8 | "https://peppe.rs/index.xml", | 9 | // "https://peppe.rs/index.xml", |
9 | "https://jvns.ca/atom.xml", | 10 | // "https://jvns.ca/atom.xml", |
10 | // "https://www.youtube.com/feeds/videos.xml?channel_id=UCuTaETsuCOkJ0H_GAztWt0Q", | 11 | // // "https://www.youtube.com/feeds/videos.xml?channel_id=UCuTaETsuCOkJ0H_GAztWt0Q", |
11 | ]; | 12 | // ]; |
12 | 13 | // | |
13 | for f in feeds { | 14 | // for f in feeds { |
14 | match manager.add_feed(f).await { | 15 | // match manager.add_feed(f).await { |
15 | Ok(s) => println!("{s}"), | 16 | // Ok(s) => println!("{s}"), |
16 | Err(e) => println!("{e}"), | 17 | // Err(e) => println!("{e}"), |
17 | } | 18 | // } |
18 | } | 19 | // } |
20 | // | ||
21 | // for entry in manager.list_entries() { | ||
22 | // println!("{entry}"); | ||
23 | // } | ||
24 | // | ||
25 | // match manager.store() { | ||
26 | // Ok(s) => println!("{s}"), | ||
27 | // Err(e) => eprintln!("{e}"), | ||
28 | // } | ||
29 | // | ||
30 | // // let mut feed = Feed::new(url); | ||
31 | // | ||
32 | // // feed.resolve().await.unwrap(); | ||
33 | // | ||
34 | // // let last_read = DateTime::parse_from_rfc2822("Mon, 16 Mar 2020 18:30:00 +0000") | ||
35 | // // .unwrap() | ||
36 | // // .with_timezone(&Utc); | ||
37 | // | ||
38 | // // feed.last_read = last_read; | ||
39 | // | ||
40 | // // for i in feed.unread().unwrap() { | ||
41 | // // println!("{}", i.title.as_ref().unwrap().content) | ||
42 | // // } | ||
43 | // } | ||
19 | 44 | ||
20 | for entry in manager.list_entries() { | 45 | #[derive(Parser)] |
21 | println!("{entry}"); | 46 | #[command(author, version, about, long_about = None)] |
22 | } | 47 | struct Cli { |
48 | #[command(subcommand)] | ||
49 | command: Option<Command>, | ||
50 | } | ||
23 | 51 | ||
24 | // let mut feed = Feed::new(url); | 52 | #[derive(Subcommand)] |
53 | enum Command { | ||
54 | Add(AddCommand), | ||
55 | List(ListCommand), | ||
56 | Pull(PullCommand), | ||
57 | } | ||
25 | 58 | ||
26 | // feed.resolve().await.unwrap(); | 59 | #[derive(Args)] |
60 | struct AddCommand { | ||
61 | url: String, | ||
62 | } | ||
27 | 63 | ||
28 | // let last_read = DateTime::parse_from_rfc2822("Mon, 16 Mar 2020 18:30:00 +0000") | 64 | #[derive(Args)] |
29 | // .unwrap() | 65 | struct ListCommand { |
30 | // .with_timezone(&Utc); | 66 | #[arg(value_enum)] |
67 | target: ListTarget, | ||
68 | } | ||
31 | 69 | ||
32 | // feed.last_read = last_read; | 70 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] |
71 | enum ListTarget { | ||
72 | Feeds, | ||
73 | Entries, | ||
74 | } | ||
33 | 75 | ||
34 | // for i in feed.unread().unwrap() { | 76 | #[derive(Args)] |
35 | // println!("{}", i.title.as_ref().unwrap().content) | 77 | struct PullCommand { |
36 | // } | 78 | target: Option<String>, |
79 | } | ||
80 | |||
81 | #[tokio::main(flavor = "current_thread")] | ||
82 | async fn main() { | ||
83 | let opts = Cli::parse(); | ||
84 | match &opts.command { | ||
85 | Some(Command::Add(AddCommand { url })) => { | ||
86 | let mut manager = Manager::load().unwrap_or_else(|e| { | ||
87 | eprintln!("{e}"); | ||
88 | Manager::default() | ||
89 | }); | ||
90 | |||
91 | manager.add_feed(&url).await.print(); | ||
92 | manager.store().print(); | ||
93 | } | ||
94 | Some(Command::List(ListCommand { target })) => { | ||
95 | let manager = Manager::load().unwrap_or_else(|e| { | ||
96 | eprintln!("{e}"); | ||
97 | Manager::default() | ||
98 | }); | ||
99 | match target { | ||
100 | ListTarget::Feeds => manager.list_feeds().for_each(|f| println!("{f}")), | ||
101 | ListTarget::Entries => manager.list_entries().for_each(|f| println!("{f}")), | ||
102 | } | ||
103 | } | ||
104 | Some(Command::Pull(PullCommand { .. })) => { | ||
105 | let mut manager = Manager::load().unwrap_or_else(|e| { | ||
106 | eprintln!("{e}"); | ||
107 | Manager::default() | ||
108 | }); | ||
109 | let (status, errors): (Vec<_>, Vec<_>) = | ||
110 | manager.pull().await.into_iter().partition(Result::is_ok); | ||
111 | status.iter().for_each(PrintResult::print); | ||
112 | errors.iter().for_each(PrintResult::print); | ||
113 | manager.store().print(); | ||
114 | } | ||
115 | _ => {} | ||
116 | } | ||
37 | } | 117 | } |
diff --git a/src/manager.rs b/src/manager.rs index 839c8dc..74f449b 100644 --- a/src/manager.rs +++ b/src/manager.rs | |||
@@ -49,15 +49,18 @@ impl Manager { | |||
49 | self.feeds.iter() | 49 | self.feeds.iter() |
50 | } | 50 | } |
51 | 51 | ||
52 | pub async fn store(&self) -> Result<StoreStatus, IOError> { | 52 | pub fn store(&self) -> Result<StoreStatus, IOError> { |
53 | let path = crate::dirs::store_path().ok_or(IOError::MissingStorePath)?; | 53 | let path = crate::dirs::store_path().ok_or(IOError::MissingStorePath)?; |
54 | let content = serde_yaml::to_string(&self.feeds)?; | 54 | let content = serde_yaml::to_string(&self.feeds)?; |
55 | std::fs::write(path, content)?; | ||
56 | 55 | ||
57 | Ok(StoreStatus::new(self.feeds.len())) | 56 | std::fs::write(&path, content)?; |
57 | |||
58 | let count = self.feeds.len(); | ||
59 | let location = path; | ||
60 | Ok(StoreStatus::new(count, location)) | ||
58 | } | 61 | } |
59 | 62 | ||
60 | pub async fn load() -> Result<Self, IOError> { | 63 | pub fn load() -> Result<Self, IOError> { |
61 | let path = crate::dirs::store_path().ok_or(IOError::MissingStorePath)?; | 64 | let path = crate::dirs::store_path().ok_or(IOError::MissingStorePath)?; |
62 | let content = std::fs::read_to_string(path)?; | 65 | let content = std::fs::read_to_string(path)?; |
63 | let feeds = serde_yaml::from_str(&content)?; | 66 | let feeds = serde_yaml::from_str(&content)?; |
diff --git a/src/status.rs b/src/status.rs index 6874e88..7e51160 100644 --- a/src/status.rs +++ b/src/status.rs | |||
@@ -1,15 +1,23 @@ | |||
1 | use std::{fmt, path::PathBuf}; | ||
2 | |||
1 | use crate::error::EntryError; | 3 | use crate::error::EntryError; |
2 | use std::fmt; | 4 | |
5 | use ansi_term::Style; | ||
3 | 6 | ||
4 | #[derive(Debug)] | 7 | #[derive(Debug)] |
5 | pub struct PullStatus { | 8 | pub struct PullStatus { |
9 | title: String, | ||
6 | count: usize, | 10 | count: usize, |
7 | errors: Vec<EntryError>, | 11 | errors: Vec<EntryError>, |
8 | } | 12 | } |
9 | 13 | ||
10 | impl PullStatus { | 14 | impl PullStatus { |
11 | pub fn new(count: usize, errors: Vec<EntryError>) -> Self { | 15 | pub fn new(title: String, count: usize, errors: Vec<EntryError>) -> Self { |
12 | Self { count, errors } | 16 | Self { |
17 | title, | ||
18 | count, | ||
19 | errors, | ||
20 | } | ||
13 | } | 21 | } |
14 | } | 22 | } |
15 | 23 | ||
@@ -17,9 +25,10 @@ impl fmt::Display for PullStatus { | |||
17 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
18 | write!( | 26 | write!( |
19 | f, | 27 | f, |
20 | "pulled {} entries with {} errors", | 28 | "{:<20}: pulled {:>4} entries with {:>4} errors", |
21 | self.count, | 29 | Style::new().bold().paint(self.title.to_ascii_lowercase()), |
22 | self.errors.len() | 30 | Style::new().bold().paint(self.count.to_string()), |
31 | Style::new().bold().paint(self.errors.len().to_string()) | ||
23 | ) | 32 | ) |
24 | } | 33 | } |
25 | } | 34 | } |
@@ -27,16 +36,24 @@ impl fmt::Display for PullStatus { | |||
27 | #[derive(Debug)] | 36 | #[derive(Debug)] |
28 | pub struct StoreStatus { | 37 | pub struct StoreStatus { |
29 | count: usize, | 38 | count: usize, |
39 | location: PathBuf, | ||
30 | } | 40 | } |
31 | 41 | ||
32 | impl StoreStatus { | 42 | impl StoreStatus { |
33 | pub fn new(count: usize) -> Self { | 43 | pub fn new(count: usize, location: PathBuf) -> Self { |
34 | Self { count } | 44 | Self { count, location } |
35 | } | 45 | } |
36 | } | 46 | } |
37 | 47 | ||
38 | impl fmt::Display for StoreStatus { | 48 | impl fmt::Display for StoreStatus { |
39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 49 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
40 | write!(f, "stored {} feeds", self.count,) | 50 | write!( |
51 | f, | ||
52 | "cached {: >4} feeds to {}", | ||
53 | Style::new().bold().paint(self.count.to_string()), | ||
54 | Style::new() | ||
55 | .bold() | ||
56 | .paint(self.location.display().to_string()) | ||
57 | ) | ||
41 | } | 58 | } |
42 | } | 59 | } |