Skip to main content

omni_dev/cli/atlassian/jira/
mod.rs

1//! JIRA CLI subcommands.
2
3pub(crate) mod attachment;
4pub(crate) mod board;
5pub(crate) mod changelog;
6pub(crate) mod comment;
7pub(crate) mod create;
8pub(crate) mod delete;
9pub(crate) mod dev;
10pub(crate) mod edit;
11pub(crate) mod field;
12pub(crate) mod link;
13pub(crate) mod project;
14pub(crate) mod read;
15pub(crate) mod search;
16pub(crate) mod sprint;
17pub(crate) mod transition;
18pub(crate) mod watcher;
19pub(crate) mod worklog;
20pub(crate) mod write;
21
22use anyhow::Result;
23use clap::{Parser, Subcommand};
24
25/// JIRA issue management, search, agile boards, and more.
26#[derive(Parser)]
27pub struct JiraCommand {
28    /// The JIRA subcommand to execute.
29    #[command(subcommand)]
30    pub command: JiraSubcommands,
31}
32
33/// JIRA subcommands.
34#[derive(Subcommand)]
35pub enum JiraSubcommands {
36    /// Fetches a JIRA issue and outputs it as JFM markdown or ADF JSON.
37    Read(read::ReadCommand),
38    /// Pushes content to a JIRA issue.
39    Write(write::WriteCommand),
40    /// Interactive fetch-edit-push cycle for a JIRA issue.
41    Edit(edit::EditCommand),
42    /// Searches JIRA issues using JQL.
43    Search(search::SearchCommand),
44    /// Creates a new JIRA issue.
45    Create(create::CreateCommand),
46    /// Lists or executes workflow transitions on a JIRA issue.
47    Transition(transition::TransitionCommand),
48    /// Manages comments on a JIRA issue.
49    Comment(comment::CommentCommand),
50    /// Deletes a JIRA issue.
51    Delete(delete::DeleteCommand),
52    /// Shows development status (linked PRs, branches, repositories) for a JIRA issue.
53    Dev(dev::DevCommand),
54    /// Lists JIRA projects.
55    Project(project::ProjectCommand),
56    /// Manages JIRA field definitions and options.
57    Field(field::FieldCommand),
58    /// Manages JIRA agile boards.
59    Board(board::BoardCommand),
60    /// Manages JIRA agile sprints.
61    Sprint(sprint::SprintCommand),
62    /// Manages JIRA issue links.
63    Link(link::LinkCommand),
64    /// Shows change history for JIRA issues.
65    Changelog(changelog::ChangelogCommand),
66    /// Downloads JIRA issue attachments.
67    Attachment(attachment::AttachmentCommand),
68    /// Manages watchers on a JIRA issue.
69    Watcher(watcher::WatcherCommand),
70    /// Manages worklogs (time tracking) on a JIRA issue.
71    Worklog(worklog::WorklogCommand),
72}
73
74impl JiraCommand {
75    /// Executes the JIRA command.
76    pub async fn execute(self) -> Result<()> {
77        match self.command {
78            JiraSubcommands::Read(cmd) => cmd.execute().await,
79            JiraSubcommands::Write(cmd) => cmd.execute().await,
80            JiraSubcommands::Edit(cmd) => cmd.execute().await,
81            JiraSubcommands::Search(cmd) => cmd.execute().await,
82            JiraSubcommands::Create(cmd) => cmd.execute().await,
83            JiraSubcommands::Transition(cmd) => cmd.execute().await,
84            JiraSubcommands::Comment(cmd) => cmd.execute().await,
85            JiraSubcommands::Delete(cmd) => cmd.execute().await,
86            JiraSubcommands::Dev(cmd) => cmd.execute().await,
87            JiraSubcommands::Project(cmd) => cmd.execute().await,
88            JiraSubcommands::Field(cmd) => cmd.execute().await,
89            JiraSubcommands::Board(cmd) => cmd.execute().await,
90            JiraSubcommands::Sprint(cmd) => cmd.execute().await,
91            JiraSubcommands::Link(cmd) => cmd.execute().await,
92            JiraSubcommands::Changelog(cmd) => cmd.execute().await,
93            JiraSubcommands::Attachment(cmd) => cmd.execute().await,
94            JiraSubcommands::Watcher(cmd) => cmd.execute().await,
95            JiraSubcommands::Worklog(cmd) => cmd.execute().await,
96        }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use crate::cli::atlassian::format::{ContentFormat, OutputFormat};
104
105    #[test]
106    fn jira_subcommands_read_variant() {
107        let cmd = JiraCommand {
108            command: JiraSubcommands::Read(read::ReadCommand {
109                key: "PROJ-1".to_string(),
110                output: None,
111                format: ContentFormat::Jfm,
112                fields: vec![],
113                all_fields: false,
114            }),
115        };
116        assert!(matches!(cmd.command, JiraSubcommands::Read(_)));
117    }
118
119    #[test]
120    fn jira_subcommands_write_variant() {
121        let cmd = JiraCommand {
122            command: JiraSubcommands::Write(write::WriteCommand {
123                key: "PROJ-1".to_string(),
124                file: None,
125                format: ContentFormat::Jfm,
126                set_fields: vec![],
127                force: false,
128                dry_run: false,
129            }),
130        };
131        assert!(matches!(cmd.command, JiraSubcommands::Write(_)));
132    }
133
134    #[test]
135    fn jira_subcommands_edit_variant() {
136        let cmd = JiraCommand {
137            command: JiraSubcommands::Edit(edit::EditCommand {
138                key: "PROJ-1".to_string(),
139            }),
140        };
141        assert!(matches!(cmd.command, JiraSubcommands::Edit(_)));
142    }
143
144    #[test]
145    fn jira_subcommands_create_variant() {
146        let cmd = JiraCommand {
147            command: JiraSubcommands::Create(create::CreateCommand {
148                file: None,
149                format: ContentFormat::Jfm,
150                project: Some("PROJ".to_string()),
151                r#type: None,
152                summary: Some("Test".to_string()),
153                set_fields: vec![],
154                dry_run: false,
155            }),
156        };
157        assert!(matches!(cmd.command, JiraSubcommands::Create(_)));
158    }
159
160    #[test]
161    fn jira_subcommands_search_variant() {
162        let cmd = JiraCommand {
163            command: JiraSubcommands::Search(search::SearchCommand {
164                jql: Some("project = PROJ".to_string()),
165                project: None,
166                assignee: None,
167                status: None,
168                limit: 50,
169                output: OutputFormat::Table,
170            }),
171        };
172        assert!(matches!(cmd.command, JiraSubcommands::Search(_)));
173    }
174
175    #[test]
176    fn jira_subcommands_transition_variant() {
177        let cmd = JiraCommand {
178            command: JiraSubcommands::Transition(transition::TransitionCommand {
179                key: "PROJ-1".to_string(),
180                transition: Some("Done".to_string()),
181                list: false,
182                output: OutputFormat::Table,
183            }),
184        };
185        assert!(matches!(cmd.command, JiraSubcommands::Transition(_)));
186    }
187
188    #[test]
189    fn jira_subcommands_comment_variant() {
190        let cmd = JiraCommand {
191            command: JiraSubcommands::Comment(comment::CommentCommand {
192                command: comment::CommentSubcommands::List(comment::ListCommand {
193                    key: "PROJ-1".to_string(),
194                    output: OutputFormat::Table,
195                    limit: 0,
196                }),
197            }),
198        };
199        assert!(matches!(cmd.command, JiraSubcommands::Comment(_)));
200    }
201
202    #[test]
203    fn jira_subcommands_delete_variant() {
204        let cmd = JiraCommand {
205            command: JiraSubcommands::Delete(delete::DeleteCommand {
206                key: "PROJ-1".to_string(),
207                force: true,
208            }),
209        };
210        assert!(matches!(cmd.command, JiraSubcommands::Delete(_)));
211    }
212
213    #[test]
214    fn jira_subcommands_dev_variant() {
215        let cmd = JiraCommand {
216            command: JiraSubcommands::Dev(dev::DevCommand {
217                key: "PROJ-1".to_string(),
218                r#type: None,
219                app: None,
220                summary: false,
221                output: OutputFormat::Table,
222            }),
223        };
224        assert!(matches!(cmd.command, JiraSubcommands::Dev(_)));
225    }
226
227    #[test]
228    fn jira_subcommands_project_variant() {
229        let cmd = JiraCommand {
230            command: JiraSubcommands::Project(project::ProjectCommand {
231                command: project::ProjectSubcommands::List(project::ListCommand {
232                    limit: 50,
233                    output: OutputFormat::Table,
234                }),
235            }),
236        };
237        assert!(matches!(cmd.command, JiraSubcommands::Project(_)));
238    }
239
240    #[test]
241    fn jira_subcommands_field_variant() {
242        let cmd = JiraCommand {
243            command: JiraSubcommands::Field(field::FieldCommand {
244                command: field::FieldSubcommands::List(field::ListCommand {
245                    search: None,
246                    output: OutputFormat::Table,
247                }),
248            }),
249        };
250        assert!(matches!(cmd.command, JiraSubcommands::Field(_)));
251    }
252
253    #[test]
254    fn jira_subcommands_board_variant() {
255        let cmd = JiraCommand {
256            command: JiraSubcommands::Board(board::BoardCommand {
257                command: board::BoardSubcommands::List(board::ListCommand {
258                    project: None,
259                    r#type: None,
260                    limit: 50,
261                    output: OutputFormat::Table,
262                }),
263            }),
264        };
265        assert!(matches!(cmd.command, JiraSubcommands::Board(_)));
266    }
267
268    #[test]
269    fn jira_subcommands_sprint_variant() {
270        let cmd = JiraCommand {
271            command: JiraSubcommands::Sprint(sprint::SprintCommand {
272                command: sprint::SprintSubcommands::List(sprint::ListCommand {
273                    board_id: 1,
274                    state: None,
275                    limit: 50,
276                    output: OutputFormat::Table,
277                }),
278            }),
279        };
280        assert!(matches!(cmd.command, JiraSubcommands::Sprint(_)));
281    }
282
283    #[test]
284    fn jira_subcommands_link_variant() {
285        let cmd = JiraCommand {
286            command: JiraSubcommands::Link(link::LinkCommand {
287                command: link::LinkSubcommands::Types(link::TypesCommand {
288                    output: OutputFormat::Table,
289                }),
290            }),
291        };
292        assert!(matches!(cmd.command, JiraSubcommands::Link(_)));
293    }
294
295    #[test]
296    fn jira_subcommands_changelog_variant() {
297        let cmd = JiraCommand {
298            command: JiraSubcommands::Changelog(changelog::ChangelogCommand {
299                keys: "PROJ-1".to_string(),
300                limit: 50,
301                output: OutputFormat::Table,
302            }),
303        };
304        assert!(matches!(cmd.command, JiraSubcommands::Changelog(_)));
305    }
306
307    #[test]
308    fn jira_subcommands_attachment_variant() {
309        let cmd = JiraCommand {
310            command: JiraSubcommands::Attachment(attachment::AttachmentCommand {
311                command: attachment::AttachmentSubcommands::Download(attachment::DownloadCommand {
312                    key: "PROJ-1".to_string(),
313                    output_dir: ".".to_string(),
314                    filter: None,
315                }),
316            }),
317        };
318        assert!(matches!(cmd.command, JiraSubcommands::Attachment(_)));
319    }
320
321    #[test]
322    fn jira_subcommands_watcher_variant() {
323        let cmd = JiraCommand {
324            command: JiraSubcommands::Watcher(watcher::WatcherCommand {
325                command: watcher::WatcherSubcommands::List(watcher::ListCommand {
326                    key: "PROJ-1".to_string(),
327                    output: OutputFormat::Table,
328                }),
329            }),
330        };
331        assert!(matches!(cmd.command, JiraSubcommands::Watcher(_)));
332    }
333
334    #[test]
335    fn jira_subcommands_worklog_variant() {
336        let cmd = JiraCommand {
337            command: JiraSubcommands::Worklog(worklog::WorklogCommand {
338                command: worklog::WorklogSubcommands::List(worklog::ListCommand {
339                    key: "PROJ-1".to_string(),
340                    limit: 50,
341                    output: OutputFormat::Table,
342                }),
343            }),
344        };
345        assert!(matches!(cmd.command, JiraSubcommands::Worklog(_)));
346    }
347}