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