1use clap::{Parser, Subcommand};
2use anyhow::Result;
3use crate::{api, article_fetcher, date_utils, feeds};
4
5#[derive(Parser)]
6#[command(name = "bbcli")]
7#[command(about = "Browse BBC News like a hacker", long_about = None)]
8#[command(version)]
9pub struct Cli {
10 #[command(subcommand)]
11 pub command: Option<Commands>,
12
13 #[arg(short, long, global = true)]
15 pub feed: Option<String>,
16}
17
18#[derive(Subcommand)]
19pub enum Commands {
20 List,
22
23 Open {
25 index: usize,
27 },
28
29 Show {
31 index: usize,
33 },
34}
35
36pub fn run_cli(cli: Cli) -> Result<()> {
37 let feed = if let Some(feed_name) = &cli.feed {
39 feeds::get_feed_by_name(feed_name)?
40 } else {
41 feeds::get_default_feed()
42 };
43
44 match cli.command {
45 Some(Commands::List) => list_headlines(&feed),
46 Some(Commands::Open { index }) => open_article(&feed, index),
47 Some(Commands::Show { index }) => show_article(&feed, index),
48 None => {
49 list_headlines(&feed)
51 }
52 }
53}
54
55fn list_headlines(feed: &feeds::Feed) -> Result<()> {
56 let stories = api::fetch_stories(&feed.url)?;
57
58 if stories.is_empty() {
59 println!("No stories available.");
60 return Ok(());
61 }
62
63 println!("# {}\n", feed.name);
64
65 for (i, story) in stories.iter().enumerate() {
66 let humanized_date = date_utils::humanize_time(&story.pub_date);
67 println!("{}. {} ({})", i + 1, story.title, humanized_date);
68 }
69
70 Ok(())
71}
72
73fn open_article(feed: &feeds::Feed, index: usize) -> Result<()> {
74 let stories = api::fetch_stories(&feed.url)?;
75
76 if index == 0 || index > stories.len() {
77 anyhow::bail!("Invalid article index: {}. Available: 1-{}", index, stories.len());
78 }
79
80 let story = &stories[index - 1];
81 println!("Opening: {}", story.title);
82 webbrowser::open(&story.link)?;
83 println!("Launched in browser.");
84
85 Ok(())
86}
87
88fn show_article(feed: &feeds::Feed, index: usize) -> Result<()> {
89 let stories = api::fetch_stories(&feed.url)?;
90
91 if index == 0 || index > stories.len() {
92 anyhow::bail!("Invalid article index: {}. Available: 1-{}", index, stories.len());
93 }
94
95 let story = &stories[index - 1];
96
97 let article_text = article_fetcher::fetch_article_content(&story.link)?;
99
100 println!("{}", article_text);
102
103 Ok(())
104}