jirust_cli/args/
commands.rs

1use crate::jira_doc_std_field;
2use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
3use regex::Regex;
4use std::error::Error;
5
6/// Command line arguments base
7/// subcommands: Config, Version
8#[derive(Parser, Debug)]
9#[command(author, version, about, long_about = None)]
10pub struct JirustCliArgs {
11    /// Subcommands
12    #[clap(subcommand)]
13    pub subcmd: Commands,
14}
15
16/// Available CLI commands
17/// Config, Issue, Project, Transition, Version
18#[derive(Subcommand, Clone, Debug)]
19pub enum Commands {
20    /// Configuration management
21    Config(ConfigArgs),
22    /// Issue management
23    Issue(IssueArgs),
24    /// Issue links management
25    Link(LinkIssueArgs),
26    /// Project management
27    Project(ProjectArgs),
28    /// Transition management
29    Transition(TransitionArgs),
30    /// Version management
31    Version(VersionArgs),
32}
33
34/// Available pagination command line arguments
35///
36/// * page_size: Option<i32>
37/// * page_offset: Option<i64>
38#[derive(Args, Clone, Debug)]
39pub struct PaginationArgs {
40    /// page size for lists
41    #[clap(
42        long,
43        short = 'l',
44        value_name = "page_size",
45        help = "page size for lists"
46    )]
47    pub page_size: Option<i32>,
48    /// page offset for list
49    #[clap(
50        long,
51        short = 's',
52        value_name = "page_offset",
53        help = "page offset for list"
54    )]
55    pub page_offset: Option<i64>,
56}
57
58/// Available output values
59/// Table, Json
60///
61/// * Table: Print output in table format
62/// * Json: Print output in json format
63#[derive(ValueEnum, Debug, Clone, Copy)]
64#[value(rename_all = "kebab-case")]
65pub enum OutputValues {
66    /// Print output in table format
67    #[value(name = "table", help = "Print output in table format")]
68    Table,
69    /// Print output in json format
70    #[value(name = "json", help = "Print output in json format")]
71    Json,
72}
73
74/// Available output types
75/// Table, Json
76///
77/// * Basic: Print basic output
78/// * Single: Print single row output
79/// * Full: Print full output
80#[derive(ValueEnum, Debug, Clone, Copy)]
81#[value(rename_all = "kebab-case")]
82pub enum OutputTypes {
83    /// Print basic output
84    #[value(name = "basic", help = "Print basic output")]
85    Basic,
86    /// Print single row output
87    #[value(name = "single", help = "Print single row output")]
88    Single,
89    /// Print full output
90    #[value(name = "full", help = "Print full output")]
91    Full,
92}
93
94/// Available output values
95///
96/// * output: Option<OutputValues> - Output format
97#[derive(Args, Clone, Debug)]
98pub struct OutputArgs {
99    /// Output format
100    #[clap(long, short = 'o', value_name = "table|json", help = "Output format")]
101    pub output_format: Option<OutputValues>,
102    /// Output type
103    #[clap(
104        long,
105        short = 'z',
106        value_name = "basic|single|full",
107        help = "Output type"
108    )]
109    pub output_type: Option<OutputTypes>,
110}
111
112/// Available configuration command line arguments
113/// cfg_act: ConfigActionValues
114///    Auth, Jira, Setup, Show
115#[derive(Args, Clone, Debug)]
116pub struct ConfigArgs {
117    /// Configuration action
118    #[arg(
119        value_name = "auth|jira|setup|show",
120        help_heading = "Configuration management"
121    )]
122    pub cfg_act: ConfigActionValues,
123}
124
125/// Available configuration action values
126/// Auth, Jira, Setup, Show
127///
128/// * Auth: Set Jira API authentication (username, apikey)
129/// * Jira: Set Jira API base URL
130/// * Setup: Setup Jira API configuration (authentication data, jira base URL, etc.)
131/// * Show: Show current configuration
132#[derive(ValueEnum, Debug, Clone, Copy)]
133#[value(rename_all = "kebab-case")]
134pub enum ConfigActionValues {
135    /// Set Jira API authentication (username, apikey)
136    #[value(name = "auth", help = "Set Jira API authentication (username, apikey)")]
137    Auth,
138    /// Set Jira API base URL
139    #[value(name = "jira", help = "Set Jira API base URL")]
140    Jira,
141    /// Setup Jira API configuration (authentication data, jira base URL, etc.)
142    #[value(
143        name = "setup",
144        help = "Setup Jira API configuration (authentication data, jira base URL, etc.)"
145    )]
146    Setup,
147    /// Show current configuration
148    #[value(name = "show", help = "Show current configuration")]
149    Show,
150}
151
152/// Available version command line arguments
153/// * version_act: VersionActionValues - Version action
154/// * project_key: String - Jira Project key
155/// * project_id: Option<i64> - Jira Project ID
156/// * version_id: Option<String> - Jira Project version ID
157/// * version_name: Option<String> - Jira Project version name
158/// * version_description: Option<String> - Jira Project version description
159/// * version_start_date: Option<String> - Jira Project version start date
160/// * version_release_date: Option<String> - Jira Project version release date
161/// * version_archived: Option<bool> - Jira Project version archived
162/// * version_released: Option<bool> - Jira Project version released
163/// * changelog_file: Option<String> - Jira Project version changelog file
164/// * transition_issues: Option<bool> - Jira Project version automatically transition issues in changelog
165/// * transition_assignee: Option<String> - Jira Project version transition assignee
166/// * pagination: PaginationArgs - Jira Project version pagination
167/// * output: OutputArgs - Jira Project version actions result output format
168///
169#[derive(Args, Clone, Debug)]
170pub struct VersionArgs {
171    /// Version action
172    #[arg(
173        value_name = "archive|create|delete|list|release|update",
174        help_heading = "Jira Project version management"
175    )]
176    pub version_act: VersionActionValues,
177    /// Jira Project key
178    #[clap(
179        long,
180        short = 'k',
181        value_name = "project_key",
182        required = true,
183        help = "Jira Project key"
184    )]
185    pub project_key: String,
186    /// Jira Project ID
187    #[clap(long, short = 'i', value_name = "project_id", help = "Jira Project ID")]
188    pub project_id: Option<i64>,
189    /// Jira Project version ID
190    #[clap(
191        long,
192        short = 'v',
193        value_name = "version_id",
194        help = "Jira Project version ID"
195    )]
196    pub version_id: Option<String>,
197    /// Jira Project version name
198    #[clap(
199        long,
200        short = 'n',
201        value_name = "version_name",
202        help = "Jira Project version name"
203    )]
204    pub version_name: Option<String>,
205    /// Jira Project version description
206    #[clap(
207        long,
208        short = 'd',
209        value_name = "version_description",
210        help = "Jira Project version description"
211    )]
212    pub version_description: Option<String>,
213    /// Jira Project version start date
214    #[clap(
215        long,
216        value_name = "version_start_date",
217        help = "Jira Project version start date"
218    )]
219    pub version_start_date: Option<String>,
220    /// Jira Project version release date
221    #[clap(
222        long,
223        value_name = "version_release_date",
224        help = "Jira Project version release date"
225    )]
226    pub version_release_date: Option<String>,
227    /// Jira Project version archived
228    #[clap(
229        long,
230        short = 'a',
231        value_name = "version_archived",
232        help = "Jira Project version archived"
233    )]
234    pub version_archived: Option<bool>,
235    /// Jira Project version released
236    #[clap(
237        long,
238        short = 'm',
239        action = ArgAction::SetTrue,
240        value_name = "version_released",
241        help = "Jira Project version released"
242    )]
243    pub version_released: Option<bool>,
244    /// Jira Project version changelog file
245    #[clap(
246        long,
247        short = 'c',
248        value_name = "changelog_file",
249        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)"
250    )]
251    pub changelog_file: Option<String>,
252    /// Jira Project version automatically transition issues in changelog
253    #[clap(
254        long,
255        short = 'r',
256        action = ArgAction::SetTrue,
257        value_name = "resolve_issues",
258        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\""
259    )]
260    pub transition_issues: Option<bool>,
261    /// Jira Project version transition assignee
262    #[clap(
263        long,
264        short = 'u',
265        value_name = "transition_assignee",
266        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)"
267    )]
268    pub transition_assignee: Option<String>,
269    /// Jira Project version pagination
270    #[clap(flatten)]
271    pub pagination: PaginationArgs,
272    /// Jira Project version actions result output format
273    #[clap(flatten)]
274    pub output: OutputArgs,
275}
276
277/// Available version action values
278/// Archive, Create, Delete, List, Release, Update
279///
280/// * Archive: Archive a Jira Project version
281/// * Create: Create a Jira Project version
282/// * Delete: Delete a Jira Project version
283/// * List: List Jira Project versions
284/// * Release: Release a Jira Project version
285/// * Update: Update a Jira Project version
286#[derive(ValueEnum, Debug, Clone, Copy)]
287#[value(rename_all = "kebab-case")]
288pub enum VersionActionValues {
289    /// Archive a Jira Project version
290    #[value(name = "archive", help = "Archive a Jira Project version")]
291    Archive,
292    /// Create a Jira Project version
293    #[value(name = "create", help = "Create a Jira Project version")]
294    Create,
295    /// Delete a Jira Project version
296    #[value(name = "delete", help = "Delete a Jira Project version")]
297    Delete,
298    /// List Jira Project versions
299    #[value(name = "list", help = "List Jira Project versions")]
300    List,
301    /// Release a Jira Project version
302    #[value(name = "release", help = "Release a Jira Project version")]
303    Release,
304    /// Update a Jira Project version
305    #[value(name = "update", help = "Update a Jira Project version")]
306    Update,
307}
308
309/// Available project command line arguments
310///
311/// * project_act: ProjectActionValues - Project action
312/// * project_key: Option<String> - Jira Project key
313/// * project_issue_type: Option<String> - Jira Project issue type ID
314/// * pagination: PaginationArgs - Jira Project pagination
315/// * output: OutputArgs - Jira Project actions result output format
316#[derive(Args, Clone, Debug)]
317pub struct ProjectArgs {
318    /// Project action
319    #[arg(
320        value_name = "get-issue-types|get-issue-type-fields|list",
321        help_heading = "Jira Project management",
322        required = true
323    )]
324    pub project_act: ProjectActionValues,
325    /// Jira Project key
326    #[clap(
327        long,
328        short = 'k',
329        value_name = "project_key",
330        help = "Jira Project key",
331        required = true
332    )]
333    pub project_key: Option<String>,
334    /// Jira Project issue type ID
335    #[clap(
336        long,
337        short = 'i',
338        value_name = "project_issue_type",
339        help = "Jira Project issue type ID"
340    )]
341    pub project_issue_type: Option<String>,
342    /// Jira Project pagination
343    #[clap(flatten)]
344    pub pagination: PaginationArgs,
345    /// Jira Project actions result output format
346    #[clap(flatten)]
347    pub output: OutputArgs,
348}
349
350/// Available project action values
351///
352/// * GetIssueTypes: Get Jira Project issue types by Jira project key
353/// * GetIssueTypeFields: Get Jira Project issue type fields by Jira project key and issue type ID
354/// * List: List Jira Projects
355#[derive(ValueEnum, Debug, Clone, Copy)]
356#[value(rename_all = "kebab-case")]
357pub enum ProjectActionValues {
358    /// Get Jira Project issue types by Jira project key
359    #[value(
360        name = "get-issue-types",
361        help = "Get Jira Project issue types by Jira project key"
362    )]
363    GetIssueTypes,
364    /// Get Jira Project issue type fields by Jira project key and issue type ID
365    #[value(
366        name = "get-issue-type-fields",
367        help = "Get Jira Project issue type fields by Jira project key and issue type ID"
368    )]
369    GetIssueTypeFields,
370    /// List Jira Projects
371    #[value(name = "list", help = "List Jira Projects")]
372    List,
373}
374
375/// Available issue command line arguments
376///
377/// * issue_act: IssueActionValues - Issue action
378/// * project_key: String - Jira Project key
379/// * issue_key: Option<String> - Jira Project issue key
380/// * issue_fields: Option<Vec<(String, String)>> - Jira Project issue fields
381/// * transition_to: Option<String> - Jira Project issue transition to
382/// * assignee: Option<String> - Jira Project issue assignee
383/// * pagination: PaginationArgs - Jira Project issue pagination
384/// * output: OutputArgs - Jira Project issue actions result output format
385#[derive(Args, Clone, Debug)]
386pub struct IssueArgs {
387    /// Issue action
388    #[arg(
389        value_name = "assign|create|delete|get|transition|update",
390        help_heading = "Jira Project issue management",
391        required = true
392    )]
393    pub issue_act: IssueActionValues,
394    /// Jira Project key
395    #[clap(
396        long,
397        short = 'p',
398        value_name = "project_key",
399        help = "Jira Project key",
400        required = true
401    )]
402    pub project_key: String,
403    /// Jira Project issue key
404    #[clap(
405        long,
406        short = 'i',
407        value_name = "issue_key",
408        help = "Jira Project issue key"
409    )]
410    pub issue_key: Option<String>,
411    /// Jira Project issue fields
412    #[clap(long,
413        short = 'f',
414        value_name = "issue_fields",
415        value_parser = parse_key_val::<String, String>,
416        help = "Jira Project issue fields (field_name=value)")]
417    pub issue_fields: Option<Vec<(String, String)>>,
418    /// Jira Project issue transition
419    #[clap(
420        long,
421        short = 't',
422        value_name = "transition_to",
423        help = "Jira Project issue transition to"
424    )]
425    pub transition_to: Option<String>,
426    /// Jira Project issue assignee
427    #[clap(
428        long,
429        short = 'a',
430        value_name = "assignee",
431        help = "Jira Project issue assignee"
432    )]
433    pub assignee: Option<String>,
434    /// Jira Project issue pagination
435    #[clap(flatten)]
436    pub pagination: PaginationArgs,
437    /// Jira Project issue actions result output format
438    #[clap(flatten)]
439    pub output: OutputArgs,
440}
441
442/// Available issue action values
443///
444/// * Assign: Assign a Jira Project issue
445/// * Create: Create a Jira Project issue
446/// * Delete: Delete a Jira Project issue
447/// * Get: Get a specific Jira Project issue
448/// * Transition: Transition a Jira Project issue
449/// * Update: Update a Jira Project issue
450#[derive(ValueEnum, Debug, Clone, Copy)]
451#[value(rename_all = "kebab-case")]
452pub enum IssueActionValues {
453    /// Assign a Jira Project issue
454    #[value(name = "assign", help = "Assign a Jira Project issue")]
455    Assign,
456    /// Create a Jira Project issue
457    #[value(name = "create", help = "Create a Jira Project issue")]
458    Create,
459    /// Delete a Jira Project issue
460    #[value(name = "delete", help = "Delete a Jira Project issue")]
461    Delete,
462    /// Get a specific Jira Project issue
463    #[value(name = "get", help = "Get a specific Jira Project issue")]
464    Get,
465    /// Transition a Jira Project issue
466    #[value(name = "transition", help = "Transition a Jira Project issue")]
467    Transition,
468    /// Update a Jira Project issue
469    #[value(name = "update", help = "Update a Jira Project issue")]
470    Update,
471}
472
473/// Available issues' links command line arguments
474///
475/// * link_act: LinkIssueActionValues - Jira link issue command available actions
476/// * project_key: Option<String> - Jira Project key
477/// * origin_issue_key: String - Jira origin issue link key
478/// * destination_issue_key: Option<String> - Jira destination issue link key
479/// * link_type: String - Jira issue link type
480/// * changelog_file: Option<String> - Jira Project version changelog file
481#[derive(Args, Clone, Debug)]
482pub struct LinkIssueArgs {
483    // Jira link issue command available actions
484    #[arg(
485        value_name = "create",
486        help_heading = "Jira issues links management",
487        required = true
488    )]
489    pub link_act: LinkIssueActionValues,
490    /// Jira Project key
491    #[clap(
492        long,
493        short = 'k',
494        value_name = "project_key",
495        help = "Jira Project key"
496    )]
497    pub project_key: Option<String>,
498    /// Jira origin issue link key
499    #[clap(
500        long,
501        short = 'i',
502        value_name = "issue_key",
503        help = "Jira issue link origin key",
504        required = true
505    )]
506    pub origin_issue_key: String,
507    /// Jira destination issue link key
508    #[clap(
509        long,
510        short = 'd',
511        value_name = "issue_key",
512        help = "Jira issue link destination key"
513    )]
514    pub destination_issue_key: Option<String>,
515    /// Jira issue link type
516    #[clap(
517        long,
518        short = 't',
519        value_name = "link_type",
520        help = "Jira issue link type",
521        required = true
522    )]
523    pub link_type: String,
524    /// Jira Project version changelog file
525    #[clap(
526        long,
527        short = 'c',
528        value_name = "changelog_file",
529        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)"
530    )]
531    pub changelog_file: Option<String>,
532}
533
534/// Available link issue action values
535///
536/// * Create: Create a Jira link between issues
537#[derive(ValueEnum, Debug, Clone, Copy)]
538#[value(rename_all = "kebab-case")]
539pub enum LinkIssueActionValues {
540    /// Create a Jira link between issues
541    #[value(name = "create", help = "Create a Jira link between issues")]
542    Create,
543}
544
545/// Available transition command line arguments
546///
547/// * transition_act: TransitionActionValues - Transition action
548/// * issue_key: String - Jira issue key
549/// * output: OutputArgs - Jira issue output format
550#[derive(Args, Clone, Debug)]
551pub struct TransitionArgs {
552    /// Transition action
553    #[arg(value_name = "list", help_heading = "Jira issue transition list")]
554    pub transition_act: TransitionActionValues,
555    /// Jira issue key
556    #[clap(
557        long,
558        short = 'i',
559        value_name = "issue_key",
560        help = "Jira Project issue key",
561        required = true
562    )]
563    pub issue_key: String,
564    /// Jira issue output format
565    #[clap(flatten)]
566    pub output: OutputArgs,
567}
568
569/// Available transition action values
570///
571/// * List: List Jira issue available transitions
572#[derive(ValueEnum, Debug, Clone, Copy)]
573#[value(rename_all = "kebab-case")]
574pub enum TransitionActionValues {
575    /// List Jira issue available transitions
576    #[value(name = "list", help = "List Jira issue available transitions")]
577    List,
578}
579
580/// Parse a single key-value pair
581/// Thanks to the example from the clap documentation (https://github.com/clap-rs/clap/blob/master/examples/typed-derive.rs)
582fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
583where
584    T: std::str::FromStr,
585    T::Err: Error + Send + Sync + 'static,
586    U: std::str::FromStr,
587    U::Err: Error + Send + Sync + 'static,
588{
589    let pos = s
590        .find('=')
591        .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
592    Ok((
593        s[..pos].parse()?,
594        manage_jira_document_field(s[pos + 1..].to_string()).parse()?,
595    ))
596}
597
598/// Manage Jira document field
599/// Relies on the manage_jira_document_field macro to wrap the field in the correct format
600fn manage_jira_document_field(value: String) -> String {
601    let re = Regex::new(r"^jira_doc_field\[(.+)\]$").unwrap();
602    let captures = re.captures(&value);
603    let val = if Option::is_some(&captures) {
604        jira_doc_std_field![captures.unwrap().get(1).unwrap().as_str()].to_string()
605    } else {
606        value.to_string()
607    };
608    val
609}