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