jirust_cli/
lib.rs

1#[macro_use]
2extern crate prettytable;
3
4use crate::args::commands::Commands;
5use crate::executors::jira_commands_executors::jira_version_executor::VersionExecutor;
6
7use config::config_file::ConfigFile;
8use executors::config_executor::ConfigExecutor;
9use executors::jira_commands_executors::ExecJiraCommand;
10use executors::jira_commands_executors::jira_issue_executor::IssueExecutor;
11use executors::jira_commands_executors::jira_issue_link_executor::LinkIssueExecutor;
12use executors::jira_commands_executors::jira_issue_transition_executor::IssueTransitionExecutor;
13use executors::jira_commands_executors::jira_project_executor::ProjectExecutor;
14use std::io::{Error, ErrorKind};
15use utils::PrintableData;
16
17#[cfg(target_family = "wasm")]
18use args::commands::JirustCliArgs;
19#[cfg(target_family = "wasm")]
20use clap::Parser;
21#[cfg(target_family = "wasm")]
22use wasm_bindgen::{JsValue, prelude::wasm_bindgen};
23#[cfg(target_family = "wasm")]
24use wasm_bindgen_futures::js_sys;
25
26pub mod args;
27pub mod config;
28pub mod executors;
29pub mod runners;
30pub mod utils;
31
32#[cfg(test)]
33pub mod tests;
34
35/// Manages the loading of the CLI configuration
36///
37/// # Arguments
38/// * `config_file_path` - The path to the configuration file
39/// * `args` - The arguments passed to the CLI
40///
41/// # Returns
42/// * A tuple containing the configuration file and the command to execute
43///
44/// # Errors
45/// * If the configuration file is not found
46/// * If the configuration file is missing mandatory fields
47///
48/// # Examples
49///
50/// ```no_run
51/// use jirust_cli::manage_config;
52/// use jirust_cli::config::config_file::ConfigFile;
53/// use jirust_cli::args::commands::Commands;
54///
55/// # fn main() -> Result<(), std::io::Error> {
56/// let config_file_path = String::from("config.json");
57/// let cfg_data = manage_config(config_file_path)?;
58/// # Ok(())
59/// # }
60/// ```
61pub fn manage_config(config_file_path: String) -> Result<ConfigFile, Error> {
62    let cfg_data = match ConfigFile::read_from_file(config_file_path.as_str()) {
63        Ok(cfg) => cfg,
64        Err(_) => {
65            return Err(Error::new(
66                ErrorKind::NotFound,
67                "Missing basic configuration, setup mandatory!",
68            ));
69        }
70    };
71    if cfg_data.get_auth_key().is_empty() || cfg_data.get_jira_url().is_empty() {
72        Err(Error::new(
73            ErrorKind::NotFound,
74            "Missing basic configuration, setup mandatory!",
75        ))
76    } else {
77        Ok(cfg_data)
78    }
79}
80
81/// Processes the command passed to the CLI
82///
83/// # Arguments
84/// * `command` - The command to execute
85/// * `config_file_path` - The path to the configuration file (optional, for Jira commands but mandatory to setup config)
86/// * `cfg_data` - The configuration file data
87///
88/// # Returns
89/// * A Result containing the result of the command execution
90///
91/// # Errors
92/// * If the command execution fails
93///
94/// # Examples
95///
96/// ```no_run
97/// use jirust_cli::process_command;
98/// use jirust_cli::config::config_file::ConfigFile;
99/// use jirust_cli::args::commands::{Commands, VersionArgs, VersionActionValues, PaginationArgs, OutputArgs};
100///
101/// # fn main() -> Result<(), std::io::Error> {
102/// let args = VersionArgs {
103///   version_act: VersionActionValues::List,
104///   project_key: "project_key".to_string(),
105///   project_id: None,
106///   version_id: Some("97531".to_string()),
107///   version_name: Some("version_name".to_string()),
108///   version_description: Some("version_description".to_string()),
109///   version_start_date: None,
110///   version_release_date: None,
111///   version_archived: None,
112///   version_released: Some(true),
113///   changelog_file: None,
114///   pagination: PaginationArgs { page_size: Some(20), page_offset: None },
115///   output: OutputArgs { output_format: None, output_type: None },
116///   transition_assignee: None,
117///   transition_issues: None,
118/// };
119///
120/// let result = process_command(Commands::Version(args), None, ConfigFile::default());
121/// # Ok(())
122/// # }
123/// ```
124pub async fn process_command(
125    command: Commands,
126    config_file_path: Option<String>,
127    cfg_data: ConfigFile,
128) -> Result<Vec<PrintableData>, Box<dyn std::error::Error>> {
129    match command {
130        Commands::Config(args) => match config_file_path {
131            Some(path) => {
132                let config_executor = ConfigExecutor::new(path, args.cfg_act);
133                config_executor.exec_config_command(cfg_data).await
134            }
135            None => Err(Box::new(Error::new(
136                ErrorKind::NotFound,
137                "Missing config file path!",
138            ))),
139        },
140        Commands::Version(args) => {
141            let version_executor = VersionExecutor::new(cfg_data, args.version_act, args);
142            version_executor.exec_jira_command().await
143        }
144        Commands::Project(args) => {
145            let project_executor = ProjectExecutor::new(cfg_data, args.project_act, args);
146            project_executor.exec_jira_command().await
147        }
148        Commands::Issue(args) => {
149            let issue_executor = IssueExecutor::new(cfg_data, args.issue_act, args);
150            issue_executor.exec_jira_command().await
151        }
152        Commands::Transition(args) => {
153            let issue_transition_executor =
154                IssueTransitionExecutor::new(cfg_data, args.transition_act, args);
155            issue_transition_executor.exec_jira_command().await
156        }
157        Commands::Link(args) => {
158            let link_issue_executor = LinkIssueExecutor::new(cfg_data, args.link_act, args);
159            link_issue_executor.exec_jira_command().await
160        }
161    }
162}
163
164#[cfg(target_family = "wasm")]
165pub fn set_panic_hook() {
166    #[cfg(feature = "console_error_panic_hook")]
167    console_error_panic_hook::set_once();
168}
169
170#[cfg(target_family = "wasm")]
171#[wasm_bindgen]
172pub async fn run(js_args: js_sys::Array, js_cfg: JsValue) -> JsValue {
173    set_panic_hook();
174
175    // Initialize the command-line arguments vector
176    // The first argument is the program name in CLI, so to correctly manage the CLI arguments with `clap` crate we must add it to the head of the vector
177    let mut args: Vec<String> = vec!["jirust-cli".to_string()];
178
179    // Then we add the arguments from the JavaScript array to the args vector
180    args.append(&mut js_args.iter().filter_map(|el| el.as_string()).collect());
181
182    let opts = match JirustCliArgs::try_parse_from(args) {
183        Ok(opts) => opts,
184        Err(err) => {
185            let err_s = format!("Error: {err}");
186            return serde_wasm_bindgen::to_value(&err_s).unwrap_or(JsValue::NULL);
187        }
188    };
189
190    let cfg_data: ConfigFile = serde_wasm_bindgen::from_value(js_cfg).expect("Config must be set!");
191
192    let result = process_command(opts.subcmd, None, cfg_data).await;
193
194    match result {
195        Ok(data) => serde_wasm_bindgen::to_value(&data).unwrap_or(JsValue::NULL),
196        Err(err) => {
197            let err_s = format!("Error: {err}");
198            serde_wasm_bindgen::to_value(&err_s).unwrap_or(JsValue::NULL)
199        }
200    }
201}