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