diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-08-07 17:52:22 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-08-07 17:52:22 +0100 |
| commit | 92c2362a9bed1bc1f256e7948e087c4102b7c4f9 (patch) | |
| tree | 700bf840ba52a8bd576afcfbe532a669f104dfcb /src/lib/cli_interactor.rs | |
| parent | 8724af191f520a822214109f75a1851856c74fd2 (diff) | |
| parent | fa7adf840ac2d78defee398a61b60888f615622a (diff) | |
Merge branch 'add-prs-to-ngit-send'
Diffstat (limited to 'src/lib/cli_interactor.rs')
| -rw-r--r-- | src/lib/cli_interactor.rs | 105 |
1 files changed, 104 insertions, 1 deletions
diff --git a/src/lib/cli_interactor.rs b/src/lib/cli_interactor.rs index 8fca81d..e944bf9 100644 --- a/src/lib/cli_interactor.rs +++ b/src/lib/cli_interactor.rs | |||
| @@ -1,5 +1,8 @@ | |||
| 1 | use anyhow::{Context, Result}; | 1 | use anyhow::{Context, Result}; |
| 2 | use dialoguer::{Confirm, Input, Password, theme::ColorfulTheme}; | 2 | use dialoguer::{ |
| 3 | Confirm, Input, Password, | ||
| 4 | theme::{ColorfulTheme, Theme}, | ||
| 5 | }; | ||
| 3 | use indicatif::TermLike; | 6 | use indicatif::TermLike; |
| 4 | #[cfg(test)] | 7 | #[cfg(test)] |
| 5 | use mockall::*; | 8 | use mockall::*; |
| @@ -236,6 +239,106 @@ impl PromptMultiChoiceParms { | |||
| 236 | } | 239 | } |
| 237 | } | 240 | } |
| 238 | 241 | ||
| 242 | pub fn multi_select_with_custom_value<F>( | ||
| 243 | prompt: &str, | ||
| 244 | custom_choice_prompt: &str, | ||
| 245 | mut choices: Vec<String>, | ||
| 246 | mut defaults: Vec<bool>, | ||
| 247 | validate_choice: F, | ||
| 248 | ) -> Result<Vec<String>> | ||
| 249 | where | ||
| 250 | F: Fn(&str) -> Result<String>, | ||
| 251 | { | ||
| 252 | let mut selected_choices = vec![]; | ||
| 253 | |||
| 254 | // Loop to allow users to add more choices | ||
| 255 | loop { | ||
| 256 | // Add 'add another' option at the end of the choices | ||
| 257 | let mut current_choices = choices.clone(); | ||
| 258 | current_choices.push(if current_choices.is_empty() { | ||
| 259 | "add".to_string() | ||
| 260 | } else { | ||
| 261 | "add another".to_string() | ||
| 262 | }); | ||
| 263 | |||
| 264 | // Create default selections based on the provided defaults | ||
| 265 | let mut current_defaults = defaults.clone(); | ||
| 266 | current_defaults.push(current_choices.len() == 1); // 'add another' should not be selected by default | ||
| 267 | |||
| 268 | // Prompt for selections | ||
| 269 | let selected_indices: Vec<usize> = Interactor::default().multi_choice( | ||
| 270 | PromptMultiChoiceParms::default() | ||
| 271 | .with_prompt(prompt) | ||
| 272 | .dont_report() | ||
| 273 | .with_choices(current_choices.clone()) | ||
| 274 | .with_defaults(current_defaults), | ||
| 275 | )?; | ||
| 276 | |||
| 277 | // Collect selected choices | ||
| 278 | selected_choices.clear(); // Clear previous selections to update | ||
| 279 | for &index in &selected_indices { | ||
| 280 | if index < choices.len() { | ||
| 281 | // Exclude 'add another' option | ||
| 282 | selected_choices.push(choices[index].clone()); | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | // Check if 'add another' was selected | ||
| 287 | if selected_indices.contains(&(choices.len())) { | ||
| 288 | // Last index is 'add another' | ||
| 289 | let mut new_choice: String; | ||
| 290 | loop { | ||
| 291 | new_choice = Interactor::default().input( | ||
| 292 | PromptInputParms::default() | ||
| 293 | .with_prompt(custom_choice_prompt) | ||
| 294 | .dont_report() | ||
| 295 | .optional(), | ||
| 296 | )?; | ||
| 297 | |||
| 298 | if new_choice.is_empty() { | ||
| 299 | break; | ||
| 300 | } | ||
| 301 | // Validate the new choice | ||
| 302 | match validate_choice(&new_choice) { | ||
| 303 | Ok(valid_choice) => { | ||
| 304 | new_choice = valid_choice; // Use the fixed version of the input | ||
| 305 | break; // Valid choice, exit the loop | ||
| 306 | } | ||
| 307 | Err(err) => { | ||
| 308 | // Inform the user about the validation error | ||
| 309 | println!("Error: {err}"); | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | // Add the new choice to the choices vector | ||
| 315 | if !new_choice.is_empty() { | ||
| 316 | choices.push(new_choice.clone()); // Add new choice to the end of the list | ||
| 317 | selected_choices.push(new_choice); // Automatically select the new choice | ||
| 318 | defaults.push(true); // Set the new choice as selected by default | ||
| 319 | } | ||
| 320 | } else { | ||
| 321 | // Exit the loop if 'add another' was not selected | ||
| 322 | break; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | Ok(selected_choices) | ||
| 327 | } | ||
| 328 | |||
| 329 | pub fn show_multi_input_prompt_success(label: &str, values: &[String]) { | ||
| 330 | let values_str: Vec<&str> = values.iter().map(std::string::String::as_str).collect(); | ||
| 331 | eprintln!("{}", { | ||
| 332 | let mut s = String::new(); | ||
| 333 | let _ = ColorfulTheme::default().format_multi_select_prompt_selection( | ||
| 334 | &mut s, | ||
| 335 | label, | ||
| 336 | &values_str, | ||
| 337 | ); | ||
| 338 | s | ||
| 339 | }); | ||
| 340 | } | ||
| 341 | |||
| 239 | #[derive(Debug, Default)] | 342 | #[derive(Debug, Default)] |
| 240 | pub struct Printer { | 343 | pub struct Printer { |
| 241 | printed_lines: Vec<String>, | 344 | printed_lines: Vec<String>, |