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)]
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)]
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)]
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)]
171pub struct VersionArgs {
172 #[arg(
174 value_name = "archive|create|delete|list|release|update",
175 help_heading = "Jira Project version management"
176 )]
177 pub version_act: VersionActionValues,
178 #[clap(
180 long,
181 short = 'k',
182 value_name = "project_key",
183 required = true,
184 help = "Jira Project key"
185 )]
186 pub project_key: String,
187 #[clap(long, short = 'i', value_name = "project_id", help = "Jira Project ID")]
189 pub project_id: Option<i64>,
190 #[clap(
192 long,
193 short = 'v',
194 value_name = "version_id",
195 help = "Jira Project version ID"
196 )]
197 pub version_id: Option<String>,
198 #[clap(
200 long,
201 short = 'n',
202 value_name = "version_name",
203 help = "Jira Project version name"
204 )]
205 pub version_name: Option<String>,
206 #[clap(
208 long,
209 short = 'd',
210 value_name = "version_description",
211 help = "Jira Project version description"
212 )]
213 pub version_description: Option<String>,
214 #[clap(
216 long,
217 value_name = "version_start_date",
218 help = "Jira Project version start date"
219 )]
220 pub version_start_date: Option<String>,
221 #[clap(
223 long,
224 value_name = "version_release_date",
225 help = "Jira Project version release date"
226 )]
227 pub version_release_date: Option<String>,
228 #[clap(
230 long,
231 short = 'a',
232 value_name = "version_archived",
233 help = "Jira Project version archived"
234 )]
235 pub version_archived: Option<bool>,
236 #[clap(
238 long,
239 short = 'm',
240 action = ArgAction::SetTrue,
241 value_name = "version_released",
242 help = "Jira Project version released"
243 )]
244 pub version_released: Option<bool>,
245 #[clap(
247 long,
248 short = 'c',
249 value_name = "changelog_file",
250 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)"
251 )]
252 pub changelog_file: Option<String>,
253 #[clap(
255 long,
256 short = 'r',
257 action = ArgAction::SetTrue,
258 value_name = "resolve_issues",
259 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\""
260 )]
261 pub transition_issues: Option<bool>,
262 #[clap(
264 long,
265 short = 'u',
266 value_name = "transition_assignee",
267 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)"
268 )]
269 pub transition_assignee: Option<String>,
270 #[clap(flatten)]
272 pub pagination: PaginationArgs,
273 #[clap(flatten)]
275 pub output: OutputArgs,
276}
277
278#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize)]
289#[value(rename_all = "kebab-case")]
290pub enum VersionActionValues {
291 #[value(name = "archive", help = "Archive a Jira Project version")]
293 Archive,
294 #[value(name = "create", help = "Create a Jira Project version")]
296 Create,
297 #[value(name = "delete", help = "Delete a Jira Project version")]
299 Delete,
300 #[value(name = "list", help = "List Jira Project versions")]
302 List,
303 #[value(
305 name = "related-work-items",
306 help = "Get Project version related workitems"
307 )]
308 RelatedWorkItems,
309 #[value(name = "release", help = "Release a Jira Project version")]
311 Release,
312 #[value(name = "update", help = "Update a Jira Project version")]
314 Update,
315}
316
317#[derive(Args, Clone, Debug, Serialize, Deserialize)]
336pub struct ProjectArgs {
337 #[arg(
339 value_name = "create|get-issue-types|get-issue-type-fields|list",
340 help_heading = "Jira Project management",
341 required = true
342 )]
343 pub project_act: ProjectActionValues,
344 #[clap(
346 long,
347 short = 'k',
348 value_name = "project_key",
349 help = "Jira Project key"
350 )]
351 pub project_key: Option<String>,
352 #[clap(
354 long,
355 short = 'i',
356 value_name = "project_issue_type",
357 help = "Jira Project issue type ID"
358 )]
359 pub project_issue_type: Option<String>,
360 #[clap(long, value_name = "project_name", help = "Jira Project name")]
362 pub project_name: Option<String>,
363 #[clap(
365 long,
366 value_name = "project_description",
367 help = "Jira Project description"
368 )]
369 pub project_description: Option<String>,
370 #[clap(
372 long,
373 value_name = "project_field_configuration_id",
374 help = "Jira Project field configuration ID"
375 )]
376 pub project_field_configuration_id: Option<i64>,
377 #[clap(
379 long,
380 value_name = "project_issue_security_scheme_id",
381 help = "Jira Project issue security scheme ID"
382 )]
383 pub project_issue_security_scheme_id: Option<i64>,
384 #[clap(
386 long,
387 value_name = "project_issue_type_scheme_id",
388 help = "Jira Project issue type scheme ID"
389 )]
390 pub project_issue_type_scheme_id: Option<i64>,
391 #[clap(
393 long,
394 value_name = "project_issue_type_screen_scheme_id",
395 help = "Jira Project issue type screen scheme ID"
396 )]
397 pub project_issue_type_screen_scheme_id: Option<i64>,
398 #[clap(
400 long,
401 value_name = "project_notification_scheme_id",
402 help = "Jira Project notification scheme ID"
403 )]
404 pub project_notification_scheme_id: Option<i64>,
405 #[clap(
407 long,
408 value_name = "project_permission_scheme_id",
409 help = "Jira Project permission scheme ID"
410 )]
411 pub project_permission_scheme_id: Option<i64>,
412 #[clap(
414 long,
415 value_name = "project_workflow_scheme_id",
416 help = "Jira Project workflow scheme ID"
417 )]
418 pub project_workflow_scheme_id: Option<i64>,
419 #[clap(
421 long,
422 value_name = "project_lead_account_id",
423 help = "Jira Project lead account ID"
424 )]
425 pub project_lead_account_id: Option<String>,
426 #[clap(
428 long,
429 value_name = "project_assignee_type",
430 help = "Jira Project Assignee Type"
431 )]
432 pub project_assignee_type: Option<String>,
433 #[clap(flatten)]
435 pub pagination: PaginationArgs,
436 #[clap(flatten)]
438 pub output: OutputArgs,
439}
440
441#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize)]
448#[value(rename_all = "kebab-case")]
449pub enum ProjectActionValues {
450 #[value(name = "create", help = "Create new Jira Project")]
452 Create,
453 #[value(
455 name = "get-issue-types",
456 help = "Get Jira Project issue types by Jira project key"
457 )]
458 GetIssueTypes,
459 #[value(
461 name = "get-issue-type-fields",
462 help = "Get Jira Project issue type fields by Jira project key and issue type ID"
463 )]
464 GetIssueTypeFields,
465 #[value(name = "list", help = "List Jira Projects")]
467 List,
468}
469
470#[derive(Args, Clone, Debug, Serialize, Deserialize)]
482pub struct IssueArgs {
483 #[arg(
485 value_name = "assign|create|delete|get|search|transition|update",
486 help_heading = "Jira Project issue management",
487 required = true
488 )]
489 pub issue_act: IssueActionValues,
490 #[clap(
492 long,
493 short = 'p',
494 value_name = "project_key",
495 help = "Jira Project key"
496 )]
497 pub project_key: Option<String>,
498 #[clap(
500 long,
501 short = 'i',
502 value_name = "issue_key",
503 help = "Jira Project issue key"
504 )]
505 pub issue_key: Option<String>,
506 #[clap(long,
508 short = 'f',
509 value_name = "issue_fields",
510 value_parser = parse_key_val::<String, String>,
511 help = "Jira Project issue fields (field_name=value)")]
512 pub issue_fields: Option<Vec<(String, String)>>,
513 #[clap(
515 long,
516 short = 't',
517 value_name = "transition_to",
518 help = "Jira Project issue transition to"
519 )]
520 pub transition_to: Option<String>,
521 #[clap(
523 long,
524 short = 'a',
525 value_name = "assignee",
526 help = "Jira Project issue assignee"
527 )]
528 pub assignee: Option<String>,
529 #[clap(
531 long,
532 short = 'q',
533 value_name = "query",
534 help = "Jira Project issue query"
535 )]
536 pub query: Option<String>,
537 #[clap(flatten)]
539 pub pagination: PaginationArgs,
540 #[clap(flatten)]
542 pub output: OutputArgs,
543}
544
545#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize)]
555#[value(rename_all = "kebab-case")]
556pub enum IssueActionValues {
557 #[value(name = "assign", help = "Assign a Jira Project issue")]
559 Assign,
560 #[value(name = "create", help = "Create a Jira Project issue")]
562 Create,
563 #[value(name = "delete", help = "Delete a Jira Project issue")]
565 Delete,
566 #[value(name = "get", help = "Get a specific Jira Project issue")]
568 Get,
569 #[value(name = "search", help = "Search for Jira Project issues")]
571 Search,
572 #[value(name = "transition", help = "Transition a Jira Project issue")]
574 Transition,
575 #[value(name = "update", help = "Update a Jira Project issue")]
577 Update,
578}
579
580#[derive(Args, Clone, Debug, Serialize, Deserialize)]
589pub struct LinkIssueArgs {
590 #[arg(
592 value_name = "create",
593 help_heading = "Jira issues links management",
594 required = true
595 )]
596 pub link_act: LinkIssueActionValues,
597 #[clap(
599 long,
600 short = 'k',
601 value_name = "project_key",
602 help = "Jira Project key"
603 )]
604 pub project_key: Option<String>,
605 #[clap(
607 long,
608 short = 'i',
609 value_name = "issue_key",
610 help = "Jira issue link origin key",
611 required = true
612 )]
613 pub origin_issue_key: String,
614 #[clap(
616 long,
617 short = 'd',
618 value_name = "issue_key",
619 help = "Jira issue link destination key"
620 )]
621 pub destination_issue_key: Option<String>,
622 #[clap(
624 long,
625 short = 't',
626 value_name = "link_type",
627 help = "Jira issue link type",
628 required = true
629 )]
630 pub link_type: String,
631 #[clap(
633 long,
634 short = 'c',
635 value_name = "changelog_file",
636 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)"
637 )]
638 pub changelog_file: Option<String>,
639}
640
641#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize)]
645#[value(rename_all = "kebab-case")]
646pub enum LinkIssueActionValues {
647 #[value(name = "create", help = "Create a Jira link between issues")]
649 Create,
650}
651
652#[derive(Args, Clone, Debug, Serialize, Deserialize)]
658pub struct TransitionArgs {
659 #[arg(value_name = "list", help_heading = "Jira issue transition list")]
661 pub transition_act: TransitionActionValues,
662 #[clap(
664 long,
665 short = 'i',
666 value_name = "issue_key",
667 help = "Jira Project issue key",
668 required = true
669 )]
670 pub issue_key: String,
671 #[clap(flatten)]
673 pub output: OutputArgs,
674}
675
676#[derive(ValueEnum, Debug, Clone, Copy, Serialize, Deserialize)]
680#[value(rename_all = "kebab-case")]
681pub enum TransitionActionValues {
682 #[value(name = "list", help = "List Jira issue available transitions")]
684 List,
685}
686
687fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
690where
691 T: std::str::FromStr,
692 T::Err: Error + Send + Sync + 'static,
693 U: std::str::FromStr,
694 U::Err: Error + Send + Sync + 'static,
695{
696 let pos = s
697 .find('=')
698 .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
699 Ok((
700 s[..pos].parse()?,
701 manage_jira_document_field(s[pos + 1..].to_string()).parse()?,
702 ))
703}
704
705fn manage_jira_document_field(value: String) -> String {
708 let re = Regex::new(r"^jira_doc_field\[(.+)\]$").unwrap();
709 let captures = re.captures(&value);
710 if let Some(captures) = &captures {
711 if let Some(first_match) = captures.get(1) {
712 jira_doc_std_field![first_match.as_str()].to_string()
713 } else {
714 value.to_string()
715 }
716 } else {
717 value.to_string()
718 }
719}