1use crate::jira_doc_std_field;
2use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
3use regex::Regex;
4use serde::{Deserialize, Serialize};
5use std::error::Error;
6
7#[derive(Parser, Debug)]
10#[command(author, version, about, long_about = None)]
11pub struct JirustCliArgs {
12 #[clap(subcommand)]
14 pub subcmd: Commands,
15}
16
17#[derive(Subcommand, Clone, Debug, Serialize, Deserialize)]
20pub enum Commands {
21 Config(ConfigArgs),
23 Issue(IssueArgs),
25 Link(LinkIssueArgs),
27 Project(ProjectArgs),
29 Transition(TransitionArgs),
31 Version(VersionArgs),
33}
34
35#[derive(Args, Clone, Debug, Serialize, Deserialize)]
40pub struct PaginationArgs {
41 #[clap(
43 long,
44 short = 'l',
45 value_name = "page_size",
46 help = "page size for lists"
47 )]
48 pub page_size: Option<i32>,
49 #[clap(
51 long,
52 short = 's',
53 value_name = "page_offset",
54 help = "page offset for list"
55 )]
56 pub page_offset: Option<i64>,
57}
58
59#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
65#[value(rename_all = "kebab-case")]
66pub enum OutputValues {
67 #[value(name = "table", help = "Print output in table format")]
69 Table,
70 #[value(name = "json", help = "Print output in json format")]
72 Json,
73}
74
75#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
82#[value(rename_all = "kebab-case")]
83pub enum OutputTypes {
84 #[value(name = "basic", help = "Print basic output")]
86 Basic,
87 #[value(name = "single", help = "Print single row output")]
89 Single,
90 #[value(name = "full", help = "Print full output")]
92 Full,
93}
94
95#[derive(Args, Clone, Debug, Serialize, Deserialize)]
99pub struct OutputArgs {
100 #[clap(long, short = 'o', value_name = "table|json", help = "Output format")]
102 pub output_format: Option<OutputValues>,
103 #[clap(
105 long,
106 short = 'z',
107 value_name = "basic|single|full",
108 help = "Output type"
109 )]
110 pub output_type: Option<OutputTypes>,
111}
112
113#[derive(Args, Clone, Debug, Serialize, Deserialize)]
117pub struct ConfigArgs {
118 #[arg(
120 value_name = "auth|jira|setup|show",
121 help_heading = "Configuration management"
122 )]
123 pub cfg_act: ConfigActionValues,
124}
125
126#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
134#[value(rename_all = "kebab-case")]
135pub enum ConfigActionValues {
136 #[value(name = "auth", help = "Set Jira API authentication (username, apikey)")]
138 Auth,
139 #[value(name = "jira", help = "Set Jira API base URL")]
141 Jira,
142 #[value(
144 name = "setup",
145 help = "Setup Jira API configuration (authentication data, jira base URL, etc.)"
146 )]
147 Setup,
148 #[value(name = "show", help = "Show current configuration")]
150 Show,
151}
152
153#[derive(Args, Clone, Debug, Serialize, Deserialize)]
172pub struct VersionArgs {
173 #[arg(
175 value_name = "archive|create|delete|list|release|update",
176 help_heading = "Jira Project version management"
177 )]
178 pub version_act: VersionActionValues,
179 #[clap(
181 long,
182 short = 'k',
183 value_name = "project_key",
184 required = true,
185 help = "Jira Project key"
186 )]
187 pub project_key: String,
188 #[clap(long, short = 'i', value_name = "project_id", help = "Jira Project ID")]
190 pub project_id: Option<i64>,
191 #[clap(
193 long,
194 short = 'v',
195 value_name = "version_id",
196 help = "Jira Project version ID"
197 )]
198 pub version_id: Option<String>,
199 #[clap(
201 long,
202 short = 'n',
203 value_name = "version_name",
204 help = "Jira Project version name"
205 )]
206 pub version_name: Option<String>,
207 #[clap(
209 long,
210 short = 'd',
211 value_name = "version_description",
212 help = "Jira Project version description"
213 )]
214 pub version_description: Option<String>,
215 #[clap(
217 long,
218 value_name = "version_start_date",
219 help = "Jira Project version start date"
220 )]
221 pub version_start_date: Option<String>,
222 #[clap(
224 long,
225 value_name = "version_release_date",
226 help = "Jira Project version release date"
227 )]
228 pub version_release_date: Option<String>,
229 #[clap(
231 long,
232 short = 'a',
233 value_name = "version_archived",
234 help = "Jira Project version archived"
235 )]
236 pub version_archived: Option<bool>,
237 #[clap(
239 long,
240 short = 'm',
241 action = ArgAction::SetTrue,
242 value_name = "version_released",
243 help = "Jira Project version released"
244 )]
245 pub version_released: Option<bool>,
246 #[clap(
248 long,
249 short = 'c',
250 value_name = "changelog_file",
251 help = "changelog file path to be used for automatic description generation (if set the script detects automatically the first tagged block in the changelog and use it as description)"
252 )]
253 pub changelog_file: Option<String>,
254 #[clap(
256 long,
257 short = 'r',
258 action = ArgAction::SetTrue,
259 value_name = "resolve_issues",
260 help = "if changelog is set and this flag is set, the script will transition all issues in the changelog of the current version release to the \"resolved\" status setting the version as \"fixVersion\""
261 )]
262 pub transition_issues: Option<bool>,
263 #[clap(
265 long,
266 short = 'u',
267 value_name = "transition_assignee",
268 help = "if changelog is set and the resolve_issues flag is set, the script will assigned all the resolved issue to the user specified in this field (if not set the assignee will not be changed)"
269 )]
270 pub transition_assignee: Option<String>,
271 #[clap(flatten)]
273 pub pagination: PaginationArgs,
274 #[clap(flatten)]
276 pub output: OutputArgs,
277}
278
279#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
290#[value(rename_all = "kebab-case")]
291pub enum VersionActionValues {
292 #[value(name = "archive", help = "Archive a Jira Project version")]
294 Archive,
295 #[value(name = "create", help = "Create a Jira Project version")]
297 Create,
298 #[value(name = "delete", help = "Delete a Jira Project version")]
300 Delete,
301 #[value(name = "list", help = "List Jira Project versions")]
303 List,
304 #[value(
306 name = "related-work-items",
307 help = "Get Project version related workitems"
308 )]
309 RelatedWorkItems,
310 #[value(name = "release", help = "Release a Jira Project version")]
312 Release,
313 #[value(name = "update", help = "Update a Jira Project version")]
315 Update,
316}
317
318#[derive(Args, Clone, Debug, Serialize, Deserialize)]
337pub struct ProjectArgs {
338 #[arg(
340 value_name = "create|get-issue-types|get-issue-type-fields|list",
341 help_heading = "Jira Project management",
342 required = true
343 )]
344 pub project_act: ProjectActionValues,
345 #[clap(
347 long,
348 short = 'k',
349 value_name = "project_key",
350 help = "Jira Project key"
351 )]
352 pub project_key: Option<String>,
353 #[clap(
355 long,
356 short = 'i',
357 value_name = "project_issue_type",
358 help = "Jira Project issue type ID"
359 )]
360 pub project_issue_type: Option<String>,
361 #[clap(long, value_name = "project_name", help = "Jira Project name")]
363 pub project_name: Option<String>,
364 #[clap(
366 long,
367 value_name = "project_description",
368 help = "Jira Project description"
369 )]
370 pub project_description: Option<String>,
371 #[clap(
373 long,
374 value_name = "project_field_configuration_id",
375 help = "Jira Project field configuration ID"
376 )]
377 pub project_field_configuration_id: Option<i64>,
378 #[clap(
380 long,
381 value_name = "project_issue_security_scheme_id",
382 help = "Jira Project issue security scheme ID"
383 )]
384 pub project_issue_security_scheme_id: Option<i64>,
385 #[clap(
387 long,
388 value_name = "project_issue_type_scheme_id",
389 help = "Jira Project issue type scheme ID"
390 )]
391 pub project_issue_type_scheme_id: Option<i64>,
392 #[clap(
394 long,
395 value_name = "project_issue_type_screen_scheme_id",
396 help = "Jira Project issue type screen scheme ID"
397 )]
398 pub project_issue_type_screen_scheme_id: Option<i64>,
399 #[clap(
401 long,
402 value_name = "project_notification_scheme_id",
403 help = "Jira Project notification scheme ID"
404 )]
405 pub project_notification_scheme_id: Option<i64>,
406 #[clap(
408 long,
409 value_name = "project_permission_scheme_id",
410 help = "Jira Project permission scheme ID"
411 )]
412 pub project_permission_scheme_id: Option<i64>,
413 #[clap(
415 long,
416 value_name = "project_workflow_scheme_id",
417 help = "Jira Project workflow scheme ID"
418 )]
419 pub project_workflow_scheme_id: Option<i64>,
420 #[clap(
422 long,
423 value_name = "project_lead_account_id",
424 help = "Jira Project lead account ID"
425 )]
426 pub project_lead_account_id: Option<String>,
427 #[clap(
429 long,
430 value_name = "project_assignee_type",
431 help = "Jira Project Assignee Type"
432 )]
433 pub project_assignee_type: Option<String>,
434 #[clap(flatten)]
436 pub pagination: PaginationArgs,
437 #[clap(flatten)]
439 pub output: OutputArgs,
440}
441
442#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
449#[value(rename_all = "kebab-case")]
450pub enum ProjectActionValues {
451 #[value(name = "create", help = "Create new Jira Project")]
453 Create,
454 #[value(
456 name = "get-issue-types",
457 help = "Get Jira Project issue types by Jira project key"
458 )]
459 GetIssueTypes,
460 #[value(
462 name = "get-issue-type-fields",
463 help = "Get Jira Project issue type fields by Jira project key and issue type ID"
464 )]
465 GetIssueTypeFields,
466 #[value(name = "list", help = "List Jira Projects")]
468 List,
469}
470
471#[derive(Args, Clone, Debug, Serialize, Deserialize)]
483pub struct IssueArgs {
484 #[arg(
486 value_name = "assign|create|delete|get|search|transition|update",
487 help_heading = "Jira Project issue management",
488 required = true
489 )]
490 pub issue_act: IssueActionValues,
491 #[clap(
493 long,
494 short = 'k',
495 value_name = "project_key",
496 help = "Jira Project key"
497 )]
498 pub project_key: Option<String>,
499 #[clap(
501 long,
502 short = 'i',
503 value_name = "issue_key",
504 help = "Jira Project issue key"
505 )]
506 pub issue_key: Option<String>,
507 #[clap(long,
509 short = 'f',
510 value_name = "issue_fields",
511 value_parser = parse_key_val::<String, String>,
512 help = "Jira Project issue fields (field_name=value)")]
513 pub issue_fields: Option<Vec<(String, String)>>,
514 #[clap(
516 long,
517 short = 't',
518 value_name = "transition_to",
519 help = "Jira Project issue transition to"
520 )]
521 pub transition_to: Option<String>,
522 #[clap(
524 long,
525 short = 'a',
526 value_name = "assignee",
527 help = "Jira Project issue assignee"
528 )]
529 pub assignee: Option<String>,
530 #[clap(
532 long,
533 short = 'q',
534 value_name = "query",
535 help = "Jira Project issue query"
536 )]
537 pub query: Option<String>,
538 #[clap(flatten)]
540 pub pagination: PaginationArgs,
541 #[clap(flatten)]
543 pub output: OutputArgs,
544}
545
546#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
556#[value(rename_all = "kebab-case")]
557pub enum IssueActionValues {
558 #[value(name = "assign", help = "Assign a Jira Project issue")]
560 Assign,
561 #[value(name = "create", help = "Create a Jira Project issue")]
563 Create,
564 #[value(name = "delete", help = "Delete a Jira Project issue")]
566 Delete,
567 #[value(name = "get", help = "Get a specific Jira Project issue")]
569 Get,
570 #[value(name = "search", help = "Search for Jira Project issues")]
572 Search,
573 #[value(name = "transition", help = "Transition a Jira Project issue")]
575 Transition,
576 #[value(name = "update", help = "Update a Jira Project issue")]
578 Update,
579}
580
581#[derive(Args, Clone, Debug, Serialize, Deserialize)]
590pub struct LinkIssueArgs {
591 #[arg(
593 value_name = "create",
594 help_heading = "Jira issues links management",
595 required = true
596 )]
597 pub link_act: LinkIssueActionValues,
598 #[clap(
600 long,
601 short = 'p',
602 value_name = "project_key",
603 help = "Jira Project key"
604 )]
605 pub project_key: Option<String>,
606 #[clap(
608 long,
609 short = 'i',
610 value_name = "issue_key",
611 help = "Jira issue link origin key",
612 required = true
613 )]
614 pub origin_issue_key: String,
615 #[clap(
617 long,
618 short = 'd',
619 value_name = "issue_key",
620 help = "Jira issue link destination key"
621 )]
622 pub destination_issue_key: Option<String>,
623 #[clap(
625 long,
626 short = 't',
627 value_name = "link_type",
628 help = "Jira issue link type",
629 required = true
630 )]
631 pub link_type: String,
632 #[clap(
634 long,
635 short = 'c',
636 value_name = "changelog_file",
637 help = "changelog file path to be used for automatic issues' links generation (if set the script detects automatically the first tagged block in the changelog and use it as description)"
638 )]
639 pub changelog_file: Option<String>,
640}
641
642#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
646#[value(rename_all = "kebab-case")]
647pub enum LinkIssueActionValues {
648 #[value(name = "create", help = "Create a Jira link between issues")]
650 Create,
651}
652
653#[derive(Args, Clone, Debug, Serialize, Deserialize)]
659pub struct TransitionArgs {
660 #[arg(value_name = "list", help_heading = "Jira issue transition list")]
662 pub transition_act: TransitionActionValues,
663 #[clap(
665 long,
666 short = 'i',
667 value_name = "issue_key",
668 help = "Jira Project issue key",
669 required = true
670 )]
671 pub issue_key: String,
672 #[clap(flatten)]
674 pub output: OutputArgs,
675}
676
677#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
681#[value(rename_all = "kebab-case")]
682pub enum TransitionActionValues {
683 #[value(name = "list", help = "List Jira issue available transitions")]
685 List,
686}
687
688fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
691where
692 T: std::str::FromStr,
693 T::Err: Error + Send + Sync + 'static,
694 U: std::str::FromStr,
695 U::Err: Error + Send + Sync + 'static,
696{
697 let pos = s
698 .find('=')
699 .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
700 Ok((
701 s[..pos].parse()?,
702 manage_jira_document_field(s[pos + 1..].to_string()).parse()?,
703 ))
704}
705
706fn manage_jira_document_field(value: String) -> String {
709 let re = Regex::new(r"^jira_doc_field\[(.+)\]$").unwrap();
710 let captures = re.captures(&value);
711 if let Some(captures) = &captures {
712 if let Some(first_match) = captures.get(1) {
713 jira_doc_std_field![first_match.as_str()].to_string()
714 } else {
715 value.to_string()
716 }
717 } else {
718 value.to_string()
719 }
720}