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