jirust_cli/
lib.rs

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