1use crate::db::jira_inbox::JiraInbox;
6use crate::db::tasks::Tasks;
7use crate::libs::jira_inbox as inbox_lib;
8use crate::libs::messages::Message;
9use crate::libs::task::Task;
10use crate::libs::view::View;
11use crate::{msg_error, msg_info, msg_print, msg_success};
12use anyhow::Result;
13use clap::{Args, Subcommand};
14
15#[derive(Debug, Args)]
17pub struct InboxArgs {
18 #[command(subcommand)]
19 command: Option<InboxCommand>,
20
21 #[arg(long, short = 'n', value_name = "N")]
25 limit: Option<usize>,
26}
27
28#[derive(Debug, Subcommand)]
30enum InboxCommand {
31 #[command(about = "Sync inbox from Jira")]
33 Sync,
34
35 #[command(about = "List active inbox issues")]
37 List {
38 #[arg(long, short = 'n', value_name = "N")]
40 limit: Option<usize>,
41 },
42
43 #[command(about = "Pin an inbox issue")]
45 Pin {
46 #[arg(value_name = "KEY")]
48 key: String,
49 },
50
51 #[command(about = "Unpin an inbox issue")]
53 Unpin {
54 #[arg(value_name = "KEY")]
56 key: String,
57 },
58
59 #[command(about = "Dismiss an inbox issue")]
61 Dismiss {
62 #[arg(value_name = "KEY")]
64 key: String,
65 },
66
67 #[command(about = "Open issue URL in browser")]
69 Open {
70 #[arg(value_name = "KEY")]
72 key: String,
73 },
74
75 #[command(about = "Import issue into tasks")]
77 Take {
78 #[arg(value_name = "KEY")]
80 key: String,
81 },
82}
83
84pub async fn cmd(args: InboxArgs) -> Result<()> {
86 match args.command {
87 Some(InboxCommand::Sync) => {
88 let outcome = inbox_lib::sync_interactive(true).await?;
89 if !outcome.skipped {
90 msg_success!(Message::JiraInboxSynced {
91 fetched: outcome.fetched,
92 new_count: outcome.new_keys.len(),
93 updated: outcome.updated,
94 });
95 }
96 Ok(())
97 }
98 Some(InboxCommand::List { limit }) => list_inbox(limit.or(args.limit)),
99 Some(InboxCommand::Pin { key }) => set_pinned(&key, true),
100 Some(InboxCommand::Unpin { key }) => set_pinned(&key, false),
101 Some(InboxCommand::Dismiss { key }) => dismiss(&key),
102 Some(InboxCommand::Open { key }) => open_issue(&key),
103 Some(InboxCommand::Take { key }) => take_issue(&key),
104 None => list_inbox(args.limit),
106 }
107}
108
109fn list_inbox(limit: Option<usize>) -> Result<()> {
110 let mut items = JiraInbox::new()?.list_active()?;
111 if items.is_empty() {
112 msg_info!(Message::JiraInboxEmpty);
113 return Ok(());
114 }
115 if let Some(n) = limit {
116 items.truncate(n);
117 }
118 msg_print!(Message::JiraInboxListHeader, true);
119 View::jira_inbox(&items)
120}
121
122fn set_pinned(key: &str, pinned: bool) -> Result<()> {
123 let db = JiraInbox::new()?;
124 if !db.set_pinned(key, pinned)? {
125 msg_error!(Message::JiraInboxNotFound(key.to_string()));
126 return Ok(());
127 }
128 if pinned {
129 msg_success!(Message::JiraInboxPinned(key.to_string()));
130 } else {
131 msg_success!(Message::JiraInboxUnpinned(key.to_string()));
132 }
133 Ok(())
134}
135
136fn dismiss(key: &str) -> Result<()> {
137 let db = JiraInbox::new()?;
138 if !db.set_dismissed(key, true)? {
139 msg_error!(Message::JiraInboxNotFound(key.to_string()));
140 return Ok(());
141 }
142 msg_success!(Message::JiraInboxDismissed(key.to_string()));
143 Ok(())
144}
145
146fn open_issue(key: &str) -> Result<()> {
147 let db = JiraInbox::new()?;
148 let Some(item) = db.get_by_key(key)? else {
149 msg_error!(Message::JiraInboxNotFound(key.to_string()));
150 return Ok(());
151 };
152
153 match inbox_lib::open_url(&item.url) {
154 Ok(()) => msg_success!(Message::JiraInboxOpened(key.to_string())),
155 Err(e) => msg_error!(Message::JiraInboxOpenFailed(e.to_string())),
156 }
157 Ok(())
158}
159
160fn take_issue(key: &str) -> Result<()> {
161 let db = JiraInbox::new()?;
162 let Some(item) = db.get_by_key(key)? else {
163 msg_error!(Message::JiraInboxNotFound(key.to_string()));
164 return Ok(());
165 };
166
167 let name = format!("{} {}", item.issue_key, item.summary);
168 let task = Task::new(&name, "", Some(0));
169 Tasks::new()?.insert(&task)?;
170 let _ = db.set_dismissed(key, true)?;
171 msg_success!(Message::JiraInboxTaken(key.to_string()));
172 Ok(())
173}