From 6a18ac39bbb40ca4a5369391ab4dc8cf5747040c Mon Sep 17 00:00:00 2001 From: Warren Hood Date: Sun, 18 Aug 2024 00:38:40 +0200 Subject: [PATCH] Added a remove command --- src/main.rs | 42 +++++++++++++++++++++++++++++++++-- src/modpack.rs | 7 +++++- src/resolver.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6647365..a13f54e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,6 +60,11 @@ enum Commands { #[arg(long)] url: Option, }, + /// Remove a mod from the modpack + Remove { + /// Name of the mod to remove from the modpack + name: String, + }, } #[tokio::main(flavor = "multi_thread")] @@ -120,7 +125,7 @@ async fn main() -> Result<(), Box> { mc_modpack_meta = mc_modpack_meta.provider(provider); } mc_modpack_meta.init_project(&dir)?; - + let modpack_lock = resolver::PinnedPackMeta::load_from_directory(&dir).await?; modpack_lock.save_to_dir(&dir)?; } @@ -152,7 +157,9 @@ async fn main() -> Result<(), Box> { match resolver::PinnedPackMeta::load_from_current_directory().await { Ok(mut modpack_lock) => { - let pin_result = modpack_lock.pin_mod_and_deps(&mod_meta, &modpack_meta).await; + let pin_result = modpack_lock + .pin_mod_and_deps(&mod_meta, &modpack_meta) + .await; if let Err(e) = pin_result { revert_modpack_meta(e); } @@ -166,6 +173,37 @@ async fn main() -> Result<(), Box> { } }; } + Commands::Remove { name } => { + let mut modpack_meta = ModpackMeta::load_from_current_directory()?; + let old_modpack_meta = modpack_meta.clone(); + + modpack_meta = modpack_meta.remove_mod(&name); + modpack_meta.save_current_dir_project()?; + + let revert_modpack_meta = |e| -> ! { + let revert_result = old_modpack_meta.save_current_dir_project(); + if let Err(result) = revert_result { + panic!("Failed to revert modpack meta: {}", result); + } + panic!("Reverted modpack meta:\n{}", e); + }; + + match resolver::PinnedPackMeta::load_from_current_directory().await { + Ok(mut modpack_lock) => { + let remove_result = modpack_lock.remove_mod(&name, &modpack_meta); + if let Err(e) = remove_result { + revert_modpack_meta(e); + } + + if let Err(e) = modpack_lock.save_current_dir_lock() { + revert_modpack_meta(e); + } + } + Err(e) => { + revert_modpack_meta(e); + } + }; + } } }; diff --git a/src/modpack.rs b/src/modpack.rs index 7257ba7..06a7390 100644 --- a/src/modpack.rs +++ b/src/modpack.rs @@ -37,7 +37,7 @@ pub struct ModpackMeta { pack_name: String, pub mc_version: String, pub modloader: ModLoader, - mods: HashMap, + pub mods: HashMap, pub default_providers: Vec, } @@ -93,6 +93,11 @@ impl ModpackMeta { self } + pub fn remove_mod(mut self, mod_name: &str) -> Self { + self.mods.remove(mod_name); + self + } + pub fn init_project(&self, directory: &PathBuf) -> Result<(), Box> { let modpack_meta_file_path = directory.clone().join(PathBuf::from(MODPACK_FILENAME)); if modpack_meta_file_path.exists() { diff --git a/src/resolver.rs b/src/resolver.rs index 7f7a130..12fd42c 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -124,6 +124,65 @@ impl PinnedPackMeta { .into()) } + fn get_dependent_mods(&self, mod_name: &str) -> HashSet { + let mut dependent_mods = HashSet::new(); + + for (pinned_mod_name, pinned_mod) in self.mods.iter() { + if let Some(deps) = &pinned_mod.deps { + for dep in deps.iter() { + if dep.name == mod_name { + dependent_mods.insert(pinned_mod_name.clone()); + } + } + } + } + dependent_mods + } + + pub fn remove_mod( + &mut self, + mod_name: &str, + pack_metadata: &ModpackMeta, + ) -> Result<(), Box> { + let dependent_mods = self.get_dependent_mods(mod_name); + + if dependent_mods.len() > 0 { + return Err(format!( + "Cannot remove mod {}.The following mods depend on it:\n{:#?}", + mod_name, dependent_mods + ) + .into()); + } + let removed_mod = self.mods.remove(mod_name); + if let Some(removed_mod) = removed_mod { + println!("Removed mod {}@{}", mod_name, removed_mod.version); + } + self.prune_mods(pack_metadata)?; + Ok(()) + } + + /// Remove all mods from lockfile that aren't in the pack metadata or depended on by another mod + fn prune_mods(&mut self, pack_metadata: &ModpackMeta) -> Result<(), Box> { + let mods_to_remove: HashSet = self + .mods + .keys() + .filter(|mod_name| { + !pack_metadata.mods.contains_key(*mod_name) + && self.get_dependent_mods(mod_name).len() == 0 + }) + .map(|mod_name| mod_name.into()) + .collect(); + + for mod_name in mods_to_remove { + let removed_mod = self.mods.remove(&mod_name); + if let Some(removed_mod) = removed_mod { + println!("Pruned mod {}@{}", mod_name, removed_mod.version); + } + } + + Ok(()) + } + pub async fn init(&mut self, modpack_meta: &ModpackMeta) -> Result<(), Box> { for mod_meta in modpack_meta.iter_mods() { self.pin_mod_and_deps(mod_meta, modpack_meta).await?;