use chrono::{naive::Days, prelude::*}; use clap::{Args, Parser, Subcommand, ValueEnum}; use syn::manager::Manager; #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Cli { #[command(subcommand)] command: Option, } #[derive(Subcommand)] enum Command { /// track a new feed Add(AddCommand), /// list all entries in reverse chronological order ListEntries(ListEntriesCommand), /// list all feeds in reverse chronological order ListFeeds, /// refresh feeds Pull(PullCommand), } impl Default for Command { fn default() -> Self { Self::ListEntries(ListEntriesCommand { cutoff: None }) } } #[derive(Args)] struct AddCommand { urls: Vec, } #[derive(Args)] struct ListEntriesCommand { #[arg(value_name = "CUTOFF")] cutoff: Option, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] enum ListTarget { Feeds, Entries, } #[derive(Args)] struct PullCommand { target: Option, } 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}"), } } } #[tokio::main(flavor = "current_thread")] async fn main() { let opts = Cli::parse(); match &opts.command.unwrap_or_default() { Command::Add(AddCommand { urls }) => { let mut manager = Manager::load().unwrap_or_else(|e| { eprintln!("{e}"); Manager::default() }); let (status, errors): (Vec<_>, Vec<_>) = manager .add_feeds_and_pull(&urls) .await .into_iter() .partition(Result::is_ok); status.iter().for_each(PrintResult::print); errors.iter().for_each(PrintResult::print); manager.store().print(); } Command::ListFeeds => { let manager = Manager::load().unwrap_or_else(|e| { eprintln!("{e}"); Manager::default() }); manager.list_feeds().for_each(|f| println!("{f}")); } Command::ListEntries(ListEntriesCommand { cutoff }) => { let manager = Manager::load().unwrap_or_else(|e| { eprintln!("{e}"); Manager::default() }); manager .list_entries() .filter(|entry| { cutoff .map(|c| Utc::now() - Days::new(c) <= entry.published) .unwrap_or(true) }) .for_each(|f| println!("{f}")); } 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 .into_iter() .map(Result::unwrap) .filter(|s| !s.is_empty()) .for_each(|s| println!("{s}")); errors.iter().for_each(PrintResult::print); manager.store().print(); } } }