summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2023-05-20 13:13:05 +0100
committerAkshay <[email protected]>2023-05-20 13:13:05 +0100
commitdea860819e7c0439debfcdd3050408e1e6cef10f (patch)
treebd57346768a64ffe61b64e1abc7b134233040e1a
parentf13e8be9f32c5ca7f70dc809e3d43144f8e31396 (diff)
add base cli
-rw-r--r--Cargo.lock30
-rw-r--r--Cargo.toml4
-rw-r--r--src/error.rs2
-rw-r--r--src/feed.rs3
-rw-r--r--src/lib.rs17
-rw-r--r--src/main.rs138
-rw-r--r--src/manager.rs11
-rw-r--r--src/status.rs35
8 files changed, 195 insertions, 45 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0a65e90..d35d8ff 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -21,6 +21,15 @@ dependencies = [
21] 21]
22 22
23[[package]] 23[[package]]
24name = "ansi_term"
25version = "0.12.1"
26source = "registry+https://github.com/rust-lang/crates.io-index"
27checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
28dependencies = [
29 "winapi",
30]
31
32[[package]]
24name = "anstream" 33name = "anstream"
25version = "0.3.2" 34version = "0.3.2"
26source = "registry+https://github.com/rust-lang/crates.io-index" 35source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -134,6 +143,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
134checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" 143checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
135dependencies = [ 144dependencies = [
136 "clap_builder", 145 "clap_builder",
146 "clap_derive",
147 "once_cell",
137] 148]
138 149
139[[package]] 150[[package]]
@@ -150,6 +161,18 @@ dependencies = [
150] 161]
151 162
152[[package]] 163[[package]]
164name = "clap_derive"
165version = "4.2.0"
166source = "registry+https://github.com/rust-lang/crates.io-index"
167checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
168dependencies = [
169 "heck",
170 "proc-macro2",
171 "quote",
172 "syn 2.0.15",
173]
174
175[[package]]
153name = "clap_lex" 176name = "clap_lex"
154version = "0.4.1" 177version = "0.4.1"
155source = "registry+https://github.com/rust-lang/crates.io-index" 178source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -444,6 +467,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
444checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 467checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
445 468
446[[package]] 469[[package]]
470name = "heck"
471version = "0.4.1"
472source = "registry+https://github.com/rust-lang/crates.io-index"
473checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
474
475[[package]]
447name = "hermit-abi" 476name = "hermit-abi"
448version = "0.3.1" 477version = "0.3.1"
449source = "registry+https://github.com/rust-lang/crates.io-index" 478source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1028,6 +1057,7 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
1028name = "syn" 1057name = "syn"
1029version = "0.1.0" 1058version = "0.1.0"
1030dependencies = [ 1059dependencies = [
1060 "ansi_term",
1031 "chrono", 1061 "chrono",
1032 "clap", 1062 "clap",
1033 "feed-rs", 1063 "feed-rs",
diff --git a/Cargo.toml b/Cargo.toml
index 2894ee0..41de526 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,4 +15,6 @@ serde_yaml = "0.9"
15chrono = { version = "0.4", features = [ "std", "serde" ] } 15chrono = { version = "0.4", features = [ "std", "serde" ] }
16thiserror = "1" 16thiserror = "1"
17futures = "0.3" 17futures = "0.3"
18clap = "4.2" 18clap = { version = "4.2", features = [ "derive" ] }
19ansi_term = "0.12"
20
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();
diff --git a/src/lib.rs b/src/lib.rs
index b66a7b4..867a09c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,3 +3,20 @@ pub mod error;
3pub mod feed; 3pub mod feed;
4pub mod manager; 4pub mod manager;
5pub mod status; 5pub mod status;
6
7pub trait PrintResult {
8 fn print(&self);
9}
10
11impl<T, E> PrintResult for Result<T, E>
12where
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 @@
1use syn::manager::Manager; 1use clap::{Args, Parser, Subcommand, ValueEnum};
2use syn::{manager::Manager, PrintResult};
2 3
3#[tokio::main(flavor = "current_thread")] 4// #[tokio::main(flavor = "current_thread")]
4async 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 } 47struct Cli {
48 #[command(subcommand)]
49 command: Option<Command>,
50}
23 51
24 // let mut feed = Feed::new(url); 52#[derive(Subcommand)]
53enum Command {
54 Add(AddCommand),
55 List(ListCommand),
56 Pull(PullCommand),
57}
25 58
26 // feed.resolve().await.unwrap(); 59#[derive(Args)]
60struct 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() 65struct 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)]
71enum 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) 77struct PullCommand {
36 // } 78 target: Option<String>,
79}
80
81#[tokio::main(flavor = "current_thread")]
82async 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 @@
1use std::{fmt, path::PathBuf};
2
1use crate::error::EntryError; 3use crate::error::EntryError;
2use std::fmt; 4
5use ansi_term::Style;
3 6
4#[derive(Debug)] 7#[derive(Debug)]
5pub struct PullStatus { 8pub struct PullStatus {
9 title: String,
6 count: usize, 10 count: usize,
7 errors: Vec<EntryError>, 11 errors: Vec<EntryError>,
8} 12}
9 13
10impl PullStatus { 14impl 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)]
28pub struct StoreStatus { 37pub struct StoreStatus {
29 count: usize, 38 count: usize,
39 location: PathBuf,
30} 40}
31 41
32impl StoreStatus { 42impl 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
38impl fmt::Display for StoreStatus { 48impl 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}