1pub(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 version;
20pub(crate) mod watcher;
21pub(crate) mod worklog;
22pub(crate) mod write;
23
24use anyhow::Result;
25use clap::{Parser, Subcommand};
26
27#[derive(Parser)]
29pub struct JiraCommand {
30 #[command(subcommand)]
32 pub command: JiraSubcommands,
33}
34
35#[derive(Subcommand)]
37pub enum JiraSubcommands {
38 Read(read::ReadCommand),
40 Write(write::WriteCommand),
42 Edit(edit::EditCommand),
44 Search(search::SearchCommand),
46 Create(create::CreateCommand),
48 Transition(transition::TransitionCommand),
50 Comment(comment::CommentCommand),
52 Delete(delete::DeleteCommand),
54 Dev(dev::DevCommand),
56 Project(project::ProjectCommand),
58 Field(field::FieldCommand),
60 Board(board::BoardCommand),
62 Sprint(sprint::SprintCommand),
64 Link(link::LinkCommand),
66 Changelog(changelog::ChangelogCommand),
68 Attachment(attachment::AttachmentCommand),
70 Watcher(watcher::WatcherCommand),
72 Worklog(worklog::WorklogCommand),
74 User(user::UserCommand),
76 Version(version::VersionCommand),
78}
79
80impl JiraCommand {
81 pub async fn execute(self) -> Result<()> {
83 match self.command {
84 JiraSubcommands::Read(cmd) => cmd.execute().await,
85 JiraSubcommands::Write(cmd) => cmd.execute().await,
86 JiraSubcommands::Edit(cmd) => cmd.execute().await,
87 JiraSubcommands::Search(cmd) => cmd.execute().await,
88 JiraSubcommands::Create(cmd) => cmd.execute().await,
89 JiraSubcommands::Transition(cmd) => cmd.execute().await,
90 JiraSubcommands::Comment(cmd) => cmd.execute().await,
91 JiraSubcommands::Delete(cmd) => cmd.execute().await,
92 JiraSubcommands::Dev(cmd) => cmd.execute().await,
93 JiraSubcommands::Project(cmd) => cmd.execute().await,
94 JiraSubcommands::Field(cmd) => cmd.execute().await,
95 JiraSubcommands::Board(cmd) => cmd.execute().await,
96 JiraSubcommands::Sprint(cmd) => cmd.execute().await,
97 JiraSubcommands::Link(cmd) => cmd.execute().await,
98 JiraSubcommands::Changelog(cmd) => cmd.execute().await,
99 JiraSubcommands::Attachment(cmd) => cmd.execute().await,
100 JiraSubcommands::Watcher(cmd) => cmd.execute().await,
101 JiraSubcommands::Worklog(cmd) => cmd.execute().await,
102 JiraSubcommands::User(cmd) => cmd.execute().await,
103 JiraSubcommands::Version(cmd) => cmd.execute().await,
104 }
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::cli::atlassian::format::{ContentFormat, OutputFormat};
112
113 #[test]
114 fn jira_subcommands_read_variant() {
115 let cmd = JiraCommand {
116 command: JiraSubcommands::Read(read::ReadCommand {
117 key: "PROJ-1".to_string(),
118 output: None,
119 format: ContentFormat::Jfm,
120 fields: vec![],
121 all_fields: false,
122 }),
123 };
124 assert!(matches!(cmd.command, JiraSubcommands::Read(_)));
125 }
126
127 #[test]
128 fn jira_subcommands_write_variant() {
129 let cmd = JiraCommand {
130 command: JiraSubcommands::Write(write::WriteCommand {
131 key: "PROJ-1".to_string(),
132 file: None,
133 format: ContentFormat::Jfm,
134 no_content: false,
135 assignee: None,
136 reporter: None,
137 set_fields: vec![],
138 parent: None,
139 force: false,
140 dry_run: false,
141 }),
142 };
143 assert!(matches!(cmd.command, JiraSubcommands::Write(_)));
144 }
145
146 #[test]
147 fn jira_subcommands_edit_variant() {
148 let cmd = JiraCommand {
149 command: JiraSubcommands::Edit(edit::EditCommand {
150 key: "PROJ-1".to_string(),
151 }),
152 };
153 assert!(matches!(cmd.command, JiraSubcommands::Edit(_)));
154 }
155
156 #[test]
157 fn jira_subcommands_create_variant() {
158 let cmd = JiraCommand {
159 command: JiraSubcommands::Create(create::CreateCommand {
160 file: None,
161 format: ContentFormat::Jfm,
162 project: Some("PROJ".to_string()),
163 r#type: None,
164 summary: Some("Test".to_string()),
165 set_fields: vec![],
166 dry_run: false,
167 }),
168 };
169 assert!(matches!(cmd.command, JiraSubcommands::Create(_)));
170 }
171
172 #[test]
173 fn jira_subcommands_search_variant() {
174 let cmd = JiraCommand {
175 command: JiraSubcommands::Search(search::SearchCommand {
176 jql: Some("project = PROJ".to_string()),
177 project: None,
178 assignee: None,
179 status: None,
180 limit: 50,
181 output: OutputFormat::Table,
182 }),
183 };
184 assert!(matches!(cmd.command, JiraSubcommands::Search(_)));
185 }
186
187 #[test]
188 fn jira_subcommands_transition_variant() {
189 let cmd = JiraCommand {
190 command: JiraSubcommands::Transition(transition::TransitionCommand {
191 command: transition::TransitionSubcommands::Execute(transition::ExecuteCommand {
192 key: "PROJ-1".to_string(),
193 transition: "Done".to_string(),
194 }),
195 }),
196 };
197 assert!(matches!(cmd.command, JiraSubcommands::Transition(_)));
198 }
199
200 #[test]
201 fn jira_subcommands_comment_variant() {
202 let cmd = JiraCommand {
203 command: JiraSubcommands::Comment(comment::CommentCommand {
204 command: comment::CommentSubcommands::List(comment::ListCommand {
205 key: "PROJ-1".to_string(),
206 output: OutputFormat::Table,
207 limit: 0,
208 }),
209 }),
210 };
211 assert!(matches!(cmd.command, JiraSubcommands::Comment(_)));
212 }
213
214 #[test]
215 fn jira_subcommands_delete_variant() {
216 let cmd = JiraCommand {
217 command: JiraSubcommands::Delete(delete::DeleteCommand {
218 key: "PROJ-1".to_string(),
219 force: true,
220 dry_run: false,
221 }),
222 };
223 assert!(matches!(cmd.command, JiraSubcommands::Delete(_)));
224 }
225
226 #[test]
227 fn jira_subcommands_dev_variant() {
228 let cmd = JiraCommand {
229 command: JiraSubcommands::Dev(dev::DevCommand {
230 key: "PROJ-1".to_string(),
231 r#type: None,
232 app: None,
233 summary: false,
234 output: OutputFormat::Table,
235 }),
236 };
237 assert!(matches!(cmd.command, JiraSubcommands::Dev(_)));
238 }
239
240 #[test]
241 fn jira_subcommands_project_variant() {
242 let cmd = JiraCommand {
243 command: JiraSubcommands::Project(project::ProjectCommand {
244 command: project::ProjectSubcommands::List(project::ListCommand {
245 limit: 50,
246 output: OutputFormat::Table,
247 }),
248 }),
249 };
250 assert!(matches!(cmd.command, JiraSubcommands::Project(_)));
251 }
252
253 #[test]
254 fn jira_subcommands_field_variant() {
255 let cmd = JiraCommand {
256 command: JiraSubcommands::Field(field::FieldCommand {
257 command: field::FieldSubcommands::List(field::ListCommand {
258 search: None,
259 output: OutputFormat::Table,
260 }),
261 }),
262 };
263 assert!(matches!(cmd.command, JiraSubcommands::Field(_)));
264 }
265
266 #[test]
267 fn jira_subcommands_board_variant() {
268 let cmd = JiraCommand {
269 command: JiraSubcommands::Board(board::BoardCommand {
270 command: board::BoardSubcommands::List(board::ListCommand {
271 project: None,
272 r#type: None,
273 limit: 50,
274 output: OutputFormat::Table,
275 }),
276 }),
277 };
278 assert!(matches!(cmd.command, JiraSubcommands::Board(_)));
279 }
280
281 #[test]
282 fn jira_subcommands_sprint_variant() {
283 let cmd = JiraCommand {
284 command: JiraSubcommands::Sprint(sprint::SprintCommand {
285 command: sprint::SprintSubcommands::List(sprint::ListCommand {
286 board_id: 1,
287 state: None,
288 limit: 50,
289 output: OutputFormat::Table,
290 }),
291 }),
292 };
293 assert!(matches!(cmd.command, JiraSubcommands::Sprint(_)));
294 }
295
296 #[test]
297 fn jira_subcommands_link_variant() {
298 let cmd = JiraCommand {
299 command: JiraSubcommands::Link(link::LinkCommand {
300 command: link::LinkSubcommands::Types(link::TypesCommand {
301 output: OutputFormat::Table,
302 }),
303 }),
304 };
305 assert!(matches!(cmd.command, JiraSubcommands::Link(_)));
306 }
307
308 #[test]
309 fn jira_subcommands_changelog_variant() {
310 let cmd = JiraCommand {
311 command: JiraSubcommands::Changelog(changelog::ChangelogCommand {
312 keys: "PROJ-1".to_string(),
313 limit: 50,
314 output: OutputFormat::Table,
315 }),
316 };
317 assert!(matches!(cmd.command, JiraSubcommands::Changelog(_)));
318 }
319
320 #[test]
321 fn jira_subcommands_attachment_variant() {
322 let cmd = JiraCommand {
323 command: JiraSubcommands::Attachment(attachment::AttachmentCommand {
324 command: attachment::AttachmentSubcommands::Download(attachment::DownloadCommand {
325 key: "PROJ-1".to_string(),
326 output_dir: ".".to_string(),
327 filter: None,
328 }),
329 }),
330 };
331 assert!(matches!(cmd.command, JiraSubcommands::Attachment(_)));
332 }
333
334 #[test]
335 fn jira_subcommands_watcher_variant() {
336 let cmd = JiraCommand {
337 command: JiraSubcommands::Watcher(watcher::WatcherCommand {
338 command: watcher::WatcherSubcommands::List(watcher::ListCommand {
339 key: "PROJ-1".to_string(),
340 output: OutputFormat::Table,
341 }),
342 }),
343 };
344 assert!(matches!(cmd.command, JiraSubcommands::Watcher(_)));
345 }
346
347 #[test]
348 fn jira_subcommands_worklog_variant() {
349 let cmd = JiraCommand {
350 command: JiraSubcommands::Worklog(worklog::WorklogCommand {
351 command: worklog::WorklogSubcommands::List(worklog::ListCommand {
352 key: "PROJ-1".to_string(),
353 limit: 50,
354 output: OutputFormat::Table,
355 }),
356 }),
357 };
358 assert!(matches!(cmd.command, JiraSubcommands::Worklog(_)));
359 }
360
361 #[test]
362 fn jira_subcommands_user_variant() {
363 let cmd = JiraCommand {
364 command: JiraSubcommands::User(user::UserCommand {
365 command: user::UserSubcommands::Search(user::UserSearchCommand {
366 query: "alice".to_string(),
367 limit: 25,
368 output: OutputFormat::Table,
369 }),
370 }),
371 };
372 assert!(matches!(cmd.command, JiraSubcommands::User(_)));
373 }
374
375 #[test]
376 fn jira_subcommands_version_variant() {
377 let cmd = JiraCommand {
378 command: JiraSubcommands::Version(version::VersionCommand {
379 command: version::VersionSubcommands::List(version::ListCommand {
380 project: "PROJ".to_string(),
381 released: false,
382 unreleased: false,
383 archived: false,
384 unarchived: false,
385 output: OutputFormat::Table,
386 }),
387 }),
388 };
389 assert!(matches!(cmd.command, JiraSubcommands::Version(_)));
390 }
391
392 #[tokio::test]
396 async fn jira_command_execute_version_arm() {
397 use crate::atlassian::auth::test_util::EnvGuard;
398
399 let server = wiremock::MockServer::start().await;
400 wiremock::Mock::given(wiremock::matchers::method("GET"))
401 .and(wiremock::matchers::path(
402 "/rest/api/3/project/PROJ/versions",
403 ))
404 .respond_with(
405 wiremock::ResponseTemplate::new(200).set_body_json(serde_json::json!([
406 {"id": "1", "name": "1.0", "released": false, "archived": false}
407 ])),
408 )
409 .mount(&server)
410 .await;
411
412 let guard = EnvGuard::take();
413 let _home = guard.set_credentials(&server.uri());
414
415 let cmd = JiraCommand {
416 command: JiraSubcommands::Version(version::VersionCommand {
417 command: version::VersionSubcommands::List(version::ListCommand {
418 project: "PROJ".to_string(),
419 released: false,
420 unreleased: false,
421 archived: false,
422 unarchived: false,
423 output: OutputFormat::Yaml,
424 }),
425 }),
426 };
427 assert!(cmd.execute().await.is_ok());
428 }
429}