From 779e8cdf3fbd9a35214d8214fe9a7a8cbe3ef277 Mon Sep 17 00:00:00 2001 From: Ilesh Thiada Date: Tue, 1 Oct 2024 14:53:36 +0530 Subject: [PATCH] Improve interactive dialogues --- Cargo.lock | 19 +++---- Cargo.toml | 2 +- src/subcommands/modpack/add.rs | 12 ++-- src/subcommands/modpack/configure.rs | 3 +- src/subcommands/modpack/delete.rs | 17 +++--- src/subcommands/modpack/mod.rs | 9 ++- src/subcommands/modpack/switch.rs | 8 ++- src/subcommands/profile/configure.rs | 85 ++++++++++++++-------------- src/subcommands/profile/create.rs | 4 +- src/subcommands/profile/delete.rs | 3 +- src/subcommands/profile/mod.rs | 23 ++++++-- src/subcommands/remove.rs | 54 ++++++++++-------- src/subcommands/upgrade.rs | 26 ++------- 13 files changed, 136 insertions(+), 129 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1847c0..1a8a558 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -378,9 +378,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.22" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" +checksum = "3bbb537bb4a30b90362caddba8f360c0a56bc13d3a5570028e7197204cb54a17" dependencies = [ "jobserver", "libc", @@ -1108,9 +1108,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "hyper" @@ -1391,7 +1391,7 @@ checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libium" version = "1.32.0" -source = "git+https://github.com/gorilla-devs/libium?rev=b80b16412018e11ae7adde0727eb32065a32ddc7#b80b16412018e11ae7adde0727eb32065a32ddc7" +source = "git+https://github.com/gorilla-devs/libium?rev=2fe13808755141b6169ed32013198a7ccfdf109b#2fe13808755141b6169ed32013198a7ccfdf109b" dependencies = [ "clap", "derive_more", @@ -1981,9 +1981,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" dependencies = [ "base64 0.22.1", "bytes", @@ -2129,11 +2129,10 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] diff --git a/Cargo.toml b/Cargo.toml index 9f940d0..f54c637 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ ferinth = "2.11" colored = "2.1" futures = "0.3" inquire = "0.7" -libium = { git = "https://github.com/gorilla-devs/libium", rev = "b80b16412018e11ae7adde0727eb32065a32ddc7" } +libium = { git = "https://github.com/gorilla-devs/libium", rev = "2fe13808755141b6169ed32013198a7ccfdf109b" } # libium = { path = "../libium" } # libium = "1.32" anyhow = "1.0" diff --git a/src/subcommands/modpack/add.rs b/src/subcommands/modpack/add.rs index 798a0b0..00be6eb 100644 --- a/src/subcommands/modpack/add.rs +++ b/src/subcommands/modpack/add.rs @@ -1,6 +1,6 @@ use super::check_output_directory; use crate::TICK; -use anyhow::{anyhow, Result}; +use anyhow::{Context as _, Result}; use colored::Colorize as _; use inquire::Confirm; use libium::{ @@ -29,14 +29,15 @@ pub async fn curseforge( "Pick an output directory", "Output Directory", )? - .ok_or_else(|| anyhow!("Please pick an output directory"))?, + .context("Please pick an output directory")?, }; check_output_directory(&output_dir)?; let install_overrides = match install_overrides { Some(some) => some, None => Confirm::new("Should overrides be installed?") .with_default(true) - .prompt()?, + .prompt() + .unwrap_or_default(), }; if install_overrides { println!( @@ -74,14 +75,15 @@ pub async fn modrinth( "Pick an output directory", "Output Directory", )? - .ok_or_else(|| anyhow!("Please pick an output directory"))?, + .context("Please pick an output directory")?, }; check_output_directory(&output_dir)?; let install_overrides = match install_overrides { Some(some) => some, None => Confirm::new("Should overrides be installed?") .with_default(true) - .prompt()?, + .prompt() + .unwrap_or_default(), }; if install_overrides { println!( diff --git a/src/subcommands/modpack/configure.rs b/src/subcommands/modpack/configure.rs index bec0d49..1d1cb56 100644 --- a/src/subcommands/modpack/configure.rs +++ b/src/subcommands/modpack/configure.rs @@ -31,7 +31,8 @@ pub fn configure( } else { let install_overrides = Confirm::new("Should overrides be installed?") .with_default(modpack.install_overrides) - .prompt()?; + .prompt() + .unwrap_or(modpack.install_overrides); if install_overrides { println!( "{}", diff --git a/src/subcommands/modpack/delete.rs b/src/subcommands/modpack/delete.rs index 9b89993..e0850ec 100644 --- a/src/subcommands/modpack/delete.rs +++ b/src/subcommands/modpack/delete.rs @@ -1,13 +1,12 @@ -use std::cmp::Ordering; - use super::switch; -use anyhow::{anyhow, Result}; +use anyhow::{Context as _, Result}; use colored::Colorize as _; use inquire::Select; use libium::{ config::structs::{Config, ModpackIdentifier}, iter_ext::IterExt as _, }; +use std::cmp::Ordering; pub fn delete( config: &mut Config, @@ -20,7 +19,7 @@ pub fn delete( .modpacks .iter() .position(|modpack| modpack.name == modpack_name) - .ok_or_else(|| anyhow!("The modpack name provided does not exist"))? + .context("The modpack name provided does not exist")? } else { let modpack_names = config .modpacks @@ -39,10 +38,14 @@ pub fn delete( }) .collect_vec(); - Select::new("Select which modpack to delete", modpack_names) + if let Ok(selection) = Select::new("Select which modpack to delete", modpack_names) .with_starting_cursor(config.active_modpack) - .raw_prompt()? - .index + .raw_prompt() + { + selection.index + } else { + return Ok(()); + } }; config.modpacks.remove(selection); diff --git a/src/subcommands/modpack/mod.rs b/src/subcommands/modpack/mod.rs index d0c59fe..a3cf19b 100644 --- a/src/subcommands/modpack/mod.rs +++ b/src/subcommands/modpack/mod.rs @@ -10,7 +10,7 @@ pub use info::info; pub use switch::switch; pub use upgrade::upgrade; -use anyhow::{anyhow, ensure, Context as _, Result}; +use anyhow::{ensure, Context as _, Result}; use fs_extra::dir::{copy, CopyOptions}; use inquire::Confirm; use libium::{file_picker::pick_folder, HOME}; @@ -38,13 +38,16 @@ pub fn check_output_directory(output_dir: &Path) -> Result<()> { "There are files in the {} folder in your output directory, these will be deleted when you upgrade.", check_dir.file_name().context("Unable to get folder name")?.to_string_lossy() ); - if Confirm::new("Would like to create a backup?").prompt()? { + if Confirm::new("Would like to create a backup?") + .prompt() + .unwrap_or_default() + { let backup_dir = pick_folder( &*HOME, "Where should the backup be made?", "Output Directory", )? - .ok_or_else(|| anyhow!("Please pick an output directory"))?; + .context("Please pick an output directory")?; copy(check_dir, backup_dir, &CopyOptions::new())?; } } diff --git a/src/subcommands/modpack/switch.rs b/src/subcommands/modpack/switch.rs index ca1d261..8422385 100644 --- a/src/subcommands/modpack/switch.rs +++ b/src/subcommands/modpack/switch.rs @@ -40,10 +40,12 @@ pub fn switch(config: &mut Config, modpack_name: Option) -> Result<()> { }) .collect_vec(); - config.active_modpack = Select::new("Select which modpack to switch to", modpack_info) + if let Ok(selection) = Select::new("Select which modpack to switch to", modpack_info) .with_starting_cursor(config.active_modpack) - .raw_prompt()? - .index; + .raw_prompt() + { + config.active_modpack = selection.index; + } Ok(()) } } diff --git a/src/subcommands/profile/configure.rs b/src/subcommands/profile/configure.rs index f2ff05f..60007f7 100644 --- a/src/subcommands/profile/configure.rs +++ b/src/subcommands/profile/configure.rs @@ -56,60 +56,57 @@ pub async fn configure( "Quit", ]; - loop { - // TODO: raw_prompt_skippable - let selection = Select::new("Which setting would you like to change", items.clone()) - .raw_prompt() - .ok() - .map(|x| x.index); - - if let Some(index) = selection { - match index { - 0 => { - if let Some(dir) = pick_folder( - &profile.output_dir, - "Pick an output directory", - "Output Directory", - )? { - check_output_directory(&dir).await?; - profile.output_dir = dir; - } + while let Ok(selection) = + Select::new("Which setting would you like to change", items.clone()).raw_prompt() + { + match selection.index { + 0 => { + if let Some(dir) = pick_folder( + &profile.output_dir, + "Pick an output directory", + "Output Directory", + )? { + check_output_directory(&dir).await?; + profile.output_dir = dir; } - 1 => { - let Some(versions) = profile.filters.game_versions_mut() else { - println!("Active profile does not filter by game version"); - continue; - }; + } + 1 => { + let Some(versions) = profile.filters.game_versions_mut() else { + println!("Active profile does not filter by game version"); + continue; + }; - *versions = pick_minecraft_versions().await?; + if let Ok(selection) = pick_minecraft_versions(versions).await { + *versions = selection; } - 2 => { - let Some(loaders) = profile.filters.mod_loaders_mut() else { - println!("Active profile does not filter mod loader"); - continue; - }; - *loaders = match pick_mod_loader(loaders.first())? { + } + 2 => { + let Some(loaders) = profile.filters.mod_loaders_mut() else { + println!("Active profile does not filter mod loader"); + continue; + }; + + if let Ok(selection) = pick_mod_loader(loaders.first()) { + *loaders = match selection { ModLoader::Quilt => vec![ModLoader::Quilt, ModLoader::Fabric], loader => vec![loader], } } - 3 => { - if let Ok(new_name) = Text::new("Change the profile's name") - .with_default(&profile.name) - .prompt() - { - profile.name = new_name; - } else { - continue; - } + } + 3 => { + if let Ok(new_name) = Text::new("Change the profile's name") + .with_default(&profile.name) + .prompt() + { + profile.name = new_name; + } else { + continue; } - 4 => break, - _ => unreachable!(), } - println!(); - } else { - break; + 4 => break, + _ => unreachable!(), } + println!(); } } diff --git a/src/subcommands/profile/create.rs b/src/subcommands/profile/create.rs index 0f0db78..1756797 100644 --- a/src/subcommands/profile/create.rs +++ b/src/subcommands/profile/create.rs @@ -46,7 +46,7 @@ pub async fn create( ); if Confirm::new("Would you like to specify a custom mods directory?") .prompt() - .unwrap_or(false) + .unwrap_or_default() { if let Some(dir) = pick_folder( &selected_mods_dir, @@ -74,7 +74,7 @@ pub async fn create( Profile::new( name, selected_mods_dir, - pick_minecraft_versions().await?, + pick_minecraft_versions(&[]).await?, pick_mod_loader(None)?, ) } diff --git a/src/subcommands/profile/delete.rs b/src/subcommands/profile/delete.rs index 3b37d43..e0dd6ae 100644 --- a/src/subcommands/profile/delete.rs +++ b/src/subcommands/profile/delete.rs @@ -1,5 +1,3 @@ -use std::cmp::Ordering; - use super::switch; use anyhow::{Context as _, Result}; use colored::Colorize as _; @@ -8,6 +6,7 @@ use libium::{ config::{filters::ProfileParameters as _, structs::Config}, iter_ext::IterExt as _, }; +use std::cmp::Ordering; pub fn delete( config: &mut Config, diff --git a/src/subcommands/profile/mod.rs b/src/subcommands/profile/mod.rs index 8646f5b..5fc5427 100644 --- a/src/subcommands/profile/mod.rs +++ b/src/subcommands/profile/mod.rs @@ -9,7 +9,7 @@ pub use delete::delete; pub use info::info; pub use switch::switch; -use anyhow::{anyhow, ensure, Result}; +use anyhow::{ensure, Context as _, Result}; use colored::Colorize as _; use ferinth::Ferinth; use fs_extra::dir::{copy, CopyOptions}; @@ -28,14 +28,16 @@ pub fn pick_mod_loader(default: Option<&ModLoader>) -> Result { ModLoader::NeoForge, ModLoader::Forge, ]; - let mut picker = Select::new("Which mod loader do you use?", options.clone()); + let mut picker = Select::new("Which mod loader do you use?", options.clone()) + .without_filtering() + .without_help_message(); if let Some(default) = default { picker.starting_cursor = options.iter().position(|l| l == default).unwrap(); } Ok(picker.prompt()?) } -pub async fn pick_minecraft_versions() -> Result> { +pub async fn pick_minecraft_versions(default: &[String]) -> Result> { let mut versions = Ferinth::default().list_game_versions().await?; versions.sort_by(|a, b| { // Sort by release type (release > snapshot > beta > alpha) then in reverse chronological order @@ -43,9 +45,14 @@ pub async fn pick_minecraft_versions() -> Result> { .cmp(&b.version_type) .then(b.date.cmp(&a.date)) }); + let mut default_indices = vec![]; let display_versions = versions .iter() - .map(|v| { + .enumerate() + .map(|(i, v)| { + if default.contains(&v.version) { + default_indices.push(i); + } if v.major { v.version.bold() } else { @@ -56,6 +63,7 @@ pub async fn pick_minecraft_versions() -> Result> { let selected_versions = MultiSelect::new("Which version of Minecraft do you play?", display_versions) + .with_default(&default_indices) .raw_prompt()? .into_iter() .map(|s| s.index) @@ -97,13 +105,16 @@ pub async fn check_output_directory(output_dir: &PathBuf) -> Result<()> { println!( "There are files in your output directory, these will be deleted when you upgrade." ); - if Confirm::new("Would like to create a backup?").prompt()? { + if Confirm::new("Would like to create a backup?") + .prompt() + .unwrap_or_default() + { let backup_dir = pick_folder( &*HOME, "Where should the backup be made?", "Output Directory", )? - .ok_or_else(|| anyhow!("Please pick a backup directory"))?; + .context("Please pick a backup directory")?; create_dir_all(&backup_dir)?; copy(output_dir, backup_dir, &CopyOptions::new())?; } diff --git a/src/subcommands/remove.rs b/src/subcommands/remove.rs index 631d61e..39028bb 100644 --- a/src/subcommands/remove.rs +++ b/src/subcommands/remove.rs @@ -11,27 +11,31 @@ use libium::{ /// Else, search the given strings with the projects' name and IDs and remove them pub fn remove(profile: &mut Profile, to_remove: Vec) -> Result<()> { let mut indices_to_remove = if to_remove.is_empty() { - let mod_info = profile.mods.iter().map(|mod_| { - format!( - "{:11} {}", - match &mod_.identifier { - ModIdentifier::CurseForgeProject(id) => format!("CF {:8}", id.to_string()), - ModIdentifier::ModrinthProject(id) => format!("MR {id:8}"), - ModIdentifier::GitHubRepository(_) => "GH".to_string(), - }, - match &mod_.identifier { - ModIdentifier::ModrinthProject(_) | ModIdentifier::CurseForgeProject(_) => - mod_.name.clone(), - ModIdentifier::GitHubRepository(id) => format!("{}/{}", id.0, id.1), - }, - ) - }); - match MultiSelect::new("Select mods to remove", mod_info.collect_vec()) + let mod_info = profile + .mods + .iter() + .map(|mod_| { + format!( + "{:11} {}", + match &mod_.identifier { + ModIdentifier::CurseForgeProject(id) => format!("CF {:8}", id.to_string()), + ModIdentifier::ModrinthProject(id) => format!("MR {id:8}"), + ModIdentifier::GitHubRepository(_) => "GH".to_string(), + }, + match &mod_.identifier { + ModIdentifier::ModrinthProject(_) | ModIdentifier::CurseForgeProject(_) => + mod_.name.clone(), + ModIdentifier::GitHubRepository(id) => format!("{}/{}", id.0, id.1), + }, + ) + }) + .collect_vec(); + MultiSelect::new("Select mods to remove", mod_info.clone()) .raw_prompt_skippable()? - { - Some(items_to_remove) => items_to_remove.iter().map(|o| o.index).collect_vec(), - None => return Ok(()), // Exit if the user cancelled - } + .unwrap_or_default() + .iter() + .map(|o| o.index) + .collect_vec() } else { let mut items_to_remove = Vec::new(); for to_remove in to_remove { @@ -62,10 +66,12 @@ pub fn remove(profile: &mut Profile, to_remove: Vec) -> Result<()> { removed.push(profile.mods.swap_remove(index).name); } - println!( - "Removed {}", - removed.iter().map(|txt| txt.bold()).display(", ") - ); + if !removed.is_empty() { + println!( + "Removed {}", + removed.iter().map(|txt| txt.bold()).display(", ") + ); + } Ok(()) } diff --git a/src/subcommands/upgrade.rs b/src/subcommands/upgrade.rs index 1e9b6a6..bfc13d5 100644 --- a/src/subcommands/upgrade.rs +++ b/src/subcommands/upgrade.rs @@ -6,15 +6,14 @@ use crate::{ }; use anyhow::{anyhow, bail, Result}; use colored::Colorize as _; -use futures::{stream::FuturesUnordered, StreamExt as _, TryFutureExt as _}; +use futures::{stream::FuturesUnordered, StreamExt as _}; use indicatif::ProgressBar; use libium::{ config::{ filters::ProfileParameters as _, structs::{ModLoader, Profile}, }, - iter_ext::IterExt as _, - upgrade::{check, mod_downloadable, DownloadFile}, + upgrade::{mod_downloadable, DownloadFile}, }; use std::{ fs::read_dir, @@ -51,25 +50,10 @@ pub async fn get_platform_downloadables(profile: &Profile) -> Result<(Vec Result<(Vec { - if let Some(mod_downloadable::Error::ModrinthError( + if let mod_downloadable::Error::ModrinthError( ferinth::Error::RateLimitExceeded(_), - )) = err.downcast_ref() + ) = err { // Immediately fail if the rate limit has been exceeded progress_bar.finish_and_clear();