From dea860819e7c0439debfcdd3050408e1e6cef10f Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 20 May 2023 17:43:05 +0530 Subject: add base cli --- Cargo.lock | 30 +++++++++++++ Cargo.toml | 4 +- src/error.rs | 2 +- src/feed.rs | 3 +- src/lib.rs | 17 +++++++ src/main.rs | 138 +++++++++++++++++++++++++++++++++++++++++++++------------ src/manager.rs | 11 +++-- src/status.rs | 35 +++++++++++---- 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 @@ -20,6 +20,15 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anstream" version = "0.3.2" @@ -134,6 +143,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" dependencies = [ "clap_builder", + "clap_derive", + "once_cell", ] [[package]] @@ -149,6 +160,18 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.15", +] + [[package]] name = "clap_lex" version = "0.4.1" @@ -443,6 +466,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.1" @@ -1028,6 +1057,7 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" name = "syn" version = "0.1.0" dependencies = [ + "ansi_term", "chrono", "clap", "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" chrono = { version = "0.4", features = [ "std", "serde" ] } thiserror = "1" futures = "0.3" -clap = "4.2" +clap = { version = "4.2", features = [ "derive" ] } +ansi_term = "0.12" + 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 { #[error("error parsing entry: {0}")] Entry(#[from] EntryError), - #[error("error adding field: {0}")] + #[error("error adding feed: {0}")] Add(#[from] AddError), } 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 { .partition(Result::is_ok); // pull status + let title = self.title.clone(); let count = entries.len().saturating_sub(self.total_count()); let errors = errors.into_iter().map(Result::unwrap_err).collect(); - let pull_status = PullStatus::new(count, errors); + let pull_status = PullStatus::new(title, count, errors); // update entries 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; pub mod feed; pub mod manager; pub mod status; + +pub trait PrintResult { + fn print(&self); +} + +impl PrintResult for Result +where + T: std::fmt::Display, + E: std::fmt::Display, +{ + fn print(&self) { + match self { + Ok(ok) => println!("{ok}"), + Err(err) => eprintln!("{err}"), + } + } +} 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 @@ -use syn::manager::Manager; +use clap::{Args, Parser, Subcommand, ValueEnum}; +use syn::{manager::Manager, PrintResult}; -#[tokio::main(flavor = "current_thread")] -async fn main() { - let mut manager = Manager::default(); - - let feeds = vec![ - "https://peppe.rs/index.xml", - "https://jvns.ca/atom.xml", - // "https://www.youtube.com/feeds/videos.xml?channel_id=UCuTaETsuCOkJ0H_GAztWt0Q", - ]; - - for f in feeds { - match manager.add_feed(f).await { - Ok(s) => println!("{s}"), - Err(e) => println!("{e}"), - } - } +// #[tokio::main(flavor = "current_thread")] +// async fn main() { +// let mut manager = Manager::load().unwrap(); +// +// let feeds = vec![ +// "https://peppe.rs/index.xml", +// "https://jvns.ca/atom.xml", +// // "https://www.youtube.com/feeds/videos.xml?channel_id=UCuTaETsuCOkJ0H_GAztWt0Q", +// ]; +// +// for f in feeds { +// match manager.add_feed(f).await { +// Ok(s) => println!("{s}"), +// Err(e) => println!("{e}"), +// } +// } +// +// for entry in manager.list_entries() { +// println!("{entry}"); +// } +// +// match manager.store() { +// Ok(s) => println!("{s}"), +// Err(e) => eprintln!("{e}"), +// } +// +// // let mut feed = Feed::new(url); +// +// // feed.resolve().await.unwrap(); +// +// // let last_read = DateTime::parse_from_rfc2822("Mon, 16 Mar 2020 18:30:00 +0000") +// // .unwrap() +// // .with_timezone(&Utc); +// +// // feed.last_read = last_read; +// +// // for i in feed.unread().unwrap() { +// // println!("{}", i.title.as_ref().unwrap().content) +// // } +// } - for entry in manager.list_entries() { - println!("{entry}"); - } +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Option, +} - // let mut feed = Feed::new(url); +#[derive(Subcommand)] +enum Command { + Add(AddCommand), + List(ListCommand), + Pull(PullCommand), +} - // feed.resolve().await.unwrap(); +#[derive(Args)] +struct AddCommand { + url: String, +} - // let last_read = DateTime::parse_from_rfc2822("Mon, 16 Mar 2020 18:30:00 +0000") - // .unwrap() - // .with_timezone(&Utc); +#[derive(Args)] +struct ListCommand { + #[arg(value_enum)] + target: ListTarget, +} - // feed.last_read = last_read; +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +enum ListTarget { + Feeds, + Entries, +} - // for i in feed.unread().unwrap() { - // println!("{}", i.title.as_ref().unwrap().content) - // } +#[derive(Args)] +struct PullCommand { + target: Option, +} + +#[tokio::main(flavor = "current_thread")] +async fn main() { + let opts = Cli::parse(); + match &opts.command { + Some(Command::Add(AddCommand { url })) => { + let mut manager = Manager::load().unwrap_or_else(|e| { + eprintln!("{e}"); + Manager::default() + }); + + manager.add_feed(&url).await.print(); + manager.store().print(); + } + Some(Command::List(ListCommand { target })) => { + let manager = Manager::load().unwrap_or_else(|e| { + eprintln!("{e}"); + Manager::default() + }); + match target { + ListTarget::Feeds => manager.list_feeds().for_each(|f| println!("{f}")), + ListTarget::Entries => manager.list_entries().for_each(|f| println!("{f}")), + } + } + Some(Command::Pull(PullCommand { .. })) => { + let mut manager = Manager::load().unwrap_or_else(|e| { + eprintln!("{e}"); + Manager::default() + }); + let (status, errors): (Vec<_>, Vec<_>) = + manager.pull().await.into_iter().partition(Result::is_ok); + status.iter().for_each(PrintResult::print); + errors.iter().for_each(PrintResult::print); + manager.store().print(); + } + _ => {} + } } 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 { self.feeds.iter() } - pub async fn store(&self) -> Result { + 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)?; - Ok(StoreStatus::new(self.feeds.len())) + std::fs::write(&path, content)?; + + let count = self.feeds.len(); + let location = path; + Ok(StoreStatus::new(count, location)) } - pub async fn load() -> Result { + 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)?; 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 @@ +use std::{fmt, path::PathBuf}; + use crate::error::EntryError; -use std::fmt; + +use ansi_term::Style; #[derive(Debug)] pub struct PullStatus { + title: String, count: usize, errors: Vec, } impl PullStatus { - pub fn new(count: usize, errors: Vec) -> Self { - Self { count, errors } + pub fn new(title: String, count: usize, errors: Vec) -> Self { + Self { + title, + count, + errors, + } } } @@ -17,9 +25,10 @@ impl fmt::Display for PullStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "pulled {} entries with {} errors", - self.count, - self.errors.len() + "{:<20}: pulled {:>4} entries with {:>4} errors", + Style::new().bold().paint(self.title.to_ascii_lowercase()), + Style::new().bold().paint(self.count.to_string()), + Style::new().bold().paint(self.errors.len().to_string()) ) } } @@ -27,16 +36,24 @@ impl fmt::Display for PullStatus { #[derive(Debug)] pub struct StoreStatus { count: usize, + location: PathBuf, } impl StoreStatus { - pub fn new(count: usize) -> Self { - Self { count } + pub fn new(count: usize, location: PathBuf) -> Self { + Self { count, location } } } impl fmt::Display for StoreStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "stored {} feeds", self.count,) + write!( + f, + "cached {: >4} feeds to {}", + Style::new().bold().paint(self.count.to_string()), + Style::new() + .bold() + .paint(self.location.display().to_string()) + ) } } -- cgit v1.2.3