jirust_cli/runners/
cfg_cmd_runner.rs

1use crate::config::config_file::{AuthData, ConfigFile};
2
3use rpassword::read_password;
4use std::{fs, io::BufRead, path::Path};
5
6/// ConfigCmdRunner is a struct that holds the configuration file path
7/// and provides methods to initialize, set, and show the configuration file.
8pub struct ConfigCmdRunner {
9    cfg_file: String,
10}
11
12/// Implementation of ConfigCmdRunner
13///
14/// # Methods
15///
16/// * `new(cfg_file: String) -> ConfigCmdRunner` - creates a new instance of ConfigCmdRunner
17/// * `init_file() -> Result<(), std::io::Error>` - initializes the configuration file
18/// * `set_cfg_auth(cfg: ConfigFile) -> Result<ConfigFile, std::io::Error>` - sets the authentication data in the configuration file
19/// * `set_cfg_jira(cfg: ConfigFile) -> Result<ConfigFile, std::io::Error>` - sets the Jira URL in the configuration file
20/// * `setup_cfg(cfg: ConfigFile) -> Result<(), std::io::Error>` - sets up the configuration file
21/// * `show_cfg(cfg: ConfigFile)` - shows the configuration file
22impl ConfigCmdRunner {
23    /// Creates a new instance of ConfigCmdRunner
24    ///
25    /// # Arguments
26    ///
27    /// * `cfg_file` - a String that holds the path to the configuration file
28    ///
29    /// # Returns
30    ///
31    /// * `ConfigCmdRunner` - a new instance of ConfigCmdRunner
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// use jirust_cli::runners::cfg_cmd_runner::ConfigCmdRunner;
37    ///
38    /// let cfg_runner = ConfigCmdRunner::new("test_path/to/config/file".to_string());
39    /// ```
40    pub fn new(cfg_file: String) -> ConfigCmdRunner {
41        ConfigCmdRunner { cfg_file }
42    }
43
44    /// Ensure the CLI can create or update the configuration file before asking for input.
45    fn ensure_cfg_writable(&self) -> Result<(), std::io::Error> {
46        let path = Path::new(&self.cfg_file);
47        if let Some(parent) = path.parent() {
48            if !parent.as_os_str().is_empty() && !parent.exists() {
49                fs::create_dir_all(parent)?;
50            }
51        }
52
53        fs::OpenOptions::new()
54            .write(true)
55            .create(true)
56            .truncate(false)
57            .open(path)
58            .map(|_| ())
59    }
60
61    /// Initializes the configuration file
62    ///
63    /// # Returns
64    ///
65    /// * `Result<(), std::io::Error>` - a Result that returns an empty tuple or an error
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use jirust_cli::runners::cfg_cmd_runner::ConfigCmdRunner;
71    ///
72    /// let cfg_runner = ConfigCmdRunner::new("test_path/to/config/file".to_string());
73    /// cfg_runner.init_file();
74    /// ```
75    pub fn init_file(&self) -> Result<(), std::io::Error> {
76        let path = Path::new(&self.cfg_file);
77        fs::create_dir_all(path.parent().unwrap())?;
78        fs::File::create(path)?;
79        Ok(())
80    }
81
82    /// Sets the authentication data in the configuration file
83    ///
84    /// # Arguments
85    ///
86    /// * `cfg` - a ConfigFile that holds the configuration data
87    ///
88    /// # Returns
89    ///
90    /// * `Result<ConfigFile, std::io::Error>` - a Result that returns the updated ConfigFile or an error
91    ///
92    /// # Examples
93    ///
94    /// ```no_run
95    /// use jirust_cli::config::config_file::ConfigFile;
96    /// use jirust_cli::runners::cfg_cmd_runner::ConfigCmdRunner;
97    ///
98    /// let cfg_runner = ConfigCmdRunner::new("test_path/to/config/file".to_string());
99    /// let cfg = ConfigFile::default();
100    ///
101    /// cfg_runner.set_cfg_auth(cfg);
102    /// ```
103    pub fn set_cfg_auth(&self, cfg: ConfigFile) -> Result<ConfigFile, std::io::Error> {
104        self.ensure_cfg_writable()?;
105        println!("Your username: ");
106        let stdin = std::io::stdin();
107        let mut reader = stdin.lock();
108        self.read_auth_from_sources(cfg, &mut reader, read_password)
109    }
110
111    fn read_auth_from_sources<R, P>(
112        &self,
113        mut cfg: ConfigFile,
114        reader: &mut R,
115        mut password_reader: P,
116    ) -> Result<ConfigFile, std::io::Error>
117    where
118        R: BufRead,
119        P: FnMut() -> Result<String, std::io::Error>,
120    {
121        let mut user = String::new();
122        reader.read_line(&mut user)?;
123        println!("Your apikey: ");
124        let apikey = password_reader()?;
125        let config_data = AuthData::new(user, apikey);
126        cfg.set_auth_key(config_data.to_base64());
127        cfg.write_to_file(self.cfg_file.as_str())?;
128        Ok(cfg)
129    }
130
131    /// Sets the Jira URL in the configuration file
132    ///
133    /// # Arguments
134    ///
135    /// * `cfg` - a ConfigFile that holds the configuration data
136    ///
137    /// # Returns
138    ///
139    /// * `Result<ConfigFile, std::io::Error>` - a Result that returns the updated ConfigFile or an error
140    ///
141    /// # Examples
142    ///
143    /// ```no_run
144    /// use jirust_cli::config::config_file::ConfigFile;
145    /// use jirust_cli::runners::cfg_cmd_runner::ConfigCmdRunner;
146    ///
147    /// let cfg_runner = ConfigCmdRunner::new("test_path/to/config/file".to_string());
148    /// let cfg = ConfigFile::default();
149    /// cfg_runner.set_cfg_jira(cfg);
150    /// ```
151    pub fn set_cfg_jira(&self, cfg: ConfigFile) -> Result<ConfigFile, std::io::Error> {
152        self.ensure_cfg_writable()?;
153        println!("Your Jira instance URL: ");
154        let stdin = std::io::stdin();
155        let mut reader = stdin.lock();
156        self.read_jira_from_reader(cfg, &mut reader)
157    }
158
159    fn read_jira_from_reader<R>(
160        &self,
161        mut cfg: ConfigFile,
162        reader: &mut R,
163    ) -> Result<ConfigFile, std::io::Error>
164    where
165        R: BufRead,
166    {
167        let mut read_data = String::new();
168        reader.read_line(&mut read_data)?;
169        cfg.set_jira_url(read_data.clone());
170        read_data.clear();
171        println!("Default Jira issue resolution JSON Value: ");
172        reader.read_line(&mut read_data)?;
173        cfg.set_standard_resolution(read_data.clone());
174        read_data.clear();
175        println!("Default Jira issue resolution comment JSON: ");
176        reader.read_line(&mut read_data)?;
177        cfg.set_standard_resolution_comment(read_data);
178        cfg.write_to_file(self.cfg_file.as_str())?;
179        Ok(cfg)
180    }
181
182    /// Sets up the configuration file
183    ///
184    /// # Arguments
185    ///
186    /// * `cfg` - a ConfigFile that holds the configuration data
187    ///
188    /// # Returns
189    ///
190    /// * `Result<(), std::io::Error>` - a Result that returns an empty tuple or an error
191    ///
192    /// # Examples
193    ///
194    /// ```no_run
195    /// use jirust_cli::config::config_file::ConfigFile;
196    /// use jirust_cli::runners::cfg_cmd_runner::ConfigCmdRunner;
197    ///
198    /// let cfg_runner = ConfigCmdRunner::new("test_path/to/config/file".to_string());
199    /// let cfg = ConfigFile::default();
200    /// cfg_runner.setup_cfg(cfg);
201    /// ```
202    pub fn setup_cfg(&self, mut cfg: ConfigFile) -> Result<(), std::io::Error> {
203        self.init_file()?;
204        cfg = self.set_cfg_jira(cfg)?;
205        self.set_cfg_auth(cfg)?;
206        Ok(())
207    }
208
209    /// Shows the configuration file data
210    ///
211    /// # Arguments
212    ///
213    /// * `cfg` - a ConfigFile that holds the configuration data
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use jirust_cli::config::config_file::ConfigFile;
219    /// use jirust_cli::runners::cfg_cmd_runner::ConfigCmdRunner;
220    ///
221    /// let cfg = ConfigFile::default();
222    ///
223    /// let cfg_runner = ConfigCmdRunner::new("test_path/to/config/file".to_string());
224    /// cfg_runner.show_cfg(cfg);
225    /// ```
226    pub fn show_cfg(&self, cfg: ConfigFile) {
227        println!("Auth token: {}", cfg.get_auth_key());
228        println!("Jira URL: {}", cfg.get_jira_url());
229        println!(
230            "Jira default resolution: {:?}",
231            cfg.get_standard_resolution()
232        );
233        println!(
234            "Jira default resolution comment: {:?}",
235            cfg.get_standard_resolution_comment()
236        );
237    }
238
239    #[cfg(test)]
240    pub(crate) fn set_cfg_auth_with_reader<R, P>(
241        &self,
242        cfg: ConfigFile,
243        reader: &mut R,
244        password_reader: P,
245    ) -> Result<ConfigFile, std::io::Error>
246    where
247        R: BufRead,
248        P: FnMut() -> Result<String, std::io::Error>,
249    {
250        self.ensure_cfg_writable()?;
251        self.read_auth_from_sources(cfg, reader, password_reader)
252    }
253
254    #[cfg(test)]
255    pub(crate) fn set_cfg_jira_with_reader<R>(
256        &self,
257        cfg: ConfigFile,
258        reader: &mut R,
259    ) -> Result<ConfigFile, std::io::Error>
260    where
261        R: BufRead,
262    {
263        self.ensure_cfg_writable()?;
264        self.read_jira_from_reader(cfg, reader)
265    }
266}