From 86fa34d9a2cbabcfee00ad16f774123e05f313c2 Mon Sep 17 00:00:00 2001 From: Warren Hood Date: Sun, 18 Aug 2024 01:45:14 +0200 Subject: [PATCH] Add support for ignoring transitive dep versions --- src/main.rs | 14 +++++--- src/providers/mod.rs | 2 +- src/providers/modrinth.rs | 70 +++++++++++++++++++++++---------------- src/resolver.rs | 35 +++++++++++++++----- 4 files changed, 77 insertions(+), 44 deletions(-) diff --git a/src/main.rs b/src/main.rs index a13f54e..bede293 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,6 +59,9 @@ enum Commands { /// URL to download the mod from #[arg(long)] url: Option, + /// Whether to ignore exact transitive mod versions + #[arg(long, short, action)] + ignore_transitive_versions: bool, }, /// Remove a mod from the modpack Remove { @@ -103,7 +106,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?; + let modpack_lock = resolver::PinnedPackMeta::load_from_directory(&dir, false).await?; modpack_lock.save_to_dir(&dir)?; } Commands::New { @@ -126,13 +129,14 @@ async fn main() -> Result<(), Box> { } mc_modpack_meta.init_project(&dir)?; - let modpack_lock = resolver::PinnedPackMeta::load_from_directory(&dir).await?; + let modpack_lock = resolver::PinnedPackMeta::load_from_directory(&dir, false).await?; modpack_lock.save_to_dir(&dir)?; } Commands::Add { name, providers, url, + ignore_transitive_versions, } => { let mut modpack_meta = ModpackMeta::load_from_current_directory()?; let old_modpack_meta = modpack_meta.clone(); @@ -155,10 +159,10 @@ async fn main() -> Result<(), Box> { panic!("Reverted modpack meta:\n{}", e); }; - match resolver::PinnedPackMeta::load_from_current_directory().await { + match resolver::PinnedPackMeta::load_from_current_directory(ignore_transitive_versions).await { Ok(mut modpack_lock) => { let pin_result = modpack_lock - .pin_mod_and_deps(&mod_meta, &modpack_meta) + .pin_mod_and_deps(&mod_meta, &modpack_meta, ignore_transitive_versions) .await; if let Err(e) = pin_result { revert_modpack_meta(e); @@ -188,7 +192,7 @@ async fn main() -> Result<(), Box> { panic!("Reverted modpack meta:\n{}", e); }; - match resolver::PinnedPackMeta::load_from_current_directory().await { + match resolver::PinnedPackMeta::load_from_current_directory(false).await { Ok(mut modpack_lock) => { let remove_result = modpack_lock.remove_mod(&name, &modpack_meta); if let Err(e) = remove_result { diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 65b06f5..a4609a6 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -16,7 +16,7 @@ pub struct PinnedMod { /// Source of the files for the mod source: Vec, /// Version of mod - pub version: semver::Version, + pub version: String, /// Pinned dependencies of a pinned mod pub deps: Option> } \ No newline at end of file diff --git a/src/providers/modrinth.rs b/src/providers/modrinth.rs index d4be37a..36d40ea 100644 --- a/src/providers/modrinth.rs +++ b/src/providers/modrinth.rs @@ -64,16 +64,16 @@ struct VersionFiles { #[derive(Serialize, Deserialize, Debug)] struct ModrinthProjectVersion { - author_id: String, - date_published: String, - dependencies: Vec, + // author_id: String, + // date_published: String, + dependencies: Option>, // downloads: i64, files: Vec, // loaders: Vec, - name: String, - project_id: String, + // name: String, + // project_id: String, id: String, - version_number: semver::Version, + version_number: String, // version_type: String, } @@ -102,10 +102,12 @@ impl Modrinth { project_version: Option<&str>, pack_meta: &ModpackMeta, ) -> Result> { - let project_versions = self.get_project_versions(project_id, pack_meta).await?; + let project_versions = self + .get_project_versions(project_id, pack_meta, true) + .await?; let project_slug = self.get_project_slug(project_id).await?; - for version in project_versions.into_iter() { + for version in project_versions.iter() { if project_version.is_none() || project_version.unwrap_or("*") == version.id { return Ok(ModMeta::new(&project_slug)? .provider(ModProvider::Modrinth) @@ -126,20 +128,21 @@ impl Modrinth { mod_meta: &ModMeta, pack_meta: &ModpackMeta, ) -> Result> { - let versions = self.get_project_versions(&mod_meta.name, pack_meta).await?; + let versions = self + .get_project_versions(&mod_meta.name, pack_meta, false) + .await?; let package = if mod_meta.version == "*" { - versions.last().ok_or(format!( + versions.first().ok_or(format!( "Cannot find package {} for loader={} and mc version={}", mod_meta.name, pack_meta.modloader.to_string().to_lowercase(), pack_meta.mc_version ))? } else { - let expected_version = semver::Version::parse(&mod_meta.version)?; versions .iter() - .filter(|v| v.version_number == expected_version) + .filter(|v| v.version_number == mod_meta.version) .nth(0) .ok_or(format!( "Cannot find package {}@{}", @@ -148,15 +151,13 @@ impl Modrinth { }; let mut deps_meta = HashSet::new(); - for dep in package - .dependencies - .iter() - .filter(|dep| dep.dependency_type == "required") - { - deps_meta.insert( - self.get_mod_meta(&dep.project_id, dep.version_id.as_deref(), pack_meta) - .await?, - ); + if let Some(deps) = &package.dependencies { + for dep in deps.iter().filter(|dep| dep.dependency_type == "required") { + deps_meta.insert( + self.get_mod_meta(&dep.project_id, dep.version_id.as_deref(), pack_meta) + .await?, + ); + } } Ok(PinnedMod { @@ -170,7 +171,11 @@ impl Modrinth { }) .collect(), version: package.version_number.clone(), - deps: if package.dependencies.len() > 0 { + deps: if package + .dependencies + .as_ref() + .is_some_and(|deps| deps.len() > 0) + { Some(deps_meta) } else { None @@ -182,19 +187,26 @@ impl Modrinth { &self, mod_id: &str, pack_meta: &ModpackMeta, + ignore_game_version_and_loader: bool, // For deps we might as well let them use anything ) -> Result, Box> { - let mut project_versions: Vec = self - .client - .get(format!( - "https://api.modrinth.com/v2/project/{mod_id}/version" - )) - .query(&[ + let query_vec = if ignore_game_version_and_loader { + &vec![] + } else { + &vec![ ( "loaders", format!("[\"{}\"]", pack_meta.modloader.to_string().to_lowercase()), ), ("game_versions", format!("[\"{}\"]", pack_meta.mc_version)), - ]) + ] + }; + + let mut project_versions: Vec = self + .client + .get(format!( + "https://api.modrinth.com/v2/project/{mod_id}/version" + )) + .query(query_vec) .send() .await? .json() diff --git a/src/resolver.rs b/src/resolver.rs index 12fd42c..74b79b7 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -32,11 +32,10 @@ impl PinnedPackMeta { &mut self, mod_metadata: &ModMeta, pack_metadata: &ModpackMeta, + ignore_transitive_versions: bool, ) -> Result<(), Box> { if let Some(mod_meta) = self.mods.get(&mod_metadata.name) { - if mod_metadata.version != "*" - && semver::Version::parse(&mod_metadata.version)? == mod_meta.version - { + if mod_metadata.version != "*" && mod_metadata.version == mod_meta.version { // Skip already pinned mods // TODO: Replace * with the current mod version in the modpack meta so this doesn't get called twice for the first mod created return Ok(()); @@ -45,6 +44,11 @@ impl PinnedPackMeta { let mut deps = HashSet::from_iter(self.pin_mod(mod_metadata, pack_metadata).await?.into_iter()); + if ignore_transitive_versions { + // Ignore transitive dep versions + deps = deps.iter().map(|d| d.clone().version("*")).collect(); + } + let pinned_version = self .mods .get(&mod_metadata.name) @@ -183,9 +187,14 @@ impl PinnedPackMeta { Ok(()) } - pub async fn init(&mut self, modpack_meta: &ModpackMeta) -> Result<(), Box> { + pub async fn init( + &mut self, + modpack_meta: &ModpackMeta, + ignore_transitive_versions: bool, + ) -> Result<(), Box> { for mod_meta in modpack_meta.iter_mods() { - self.pin_mod_and_deps(mod_meta, modpack_meta).await?; + self.pin_mod_and_deps(mod_meta, modpack_meta, ignore_transitive_versions) + .await?; } Ok(()) } @@ -210,12 +219,18 @@ impl PinnedPackMeta { Ok(()) } - pub async fn load_from_directory(directory: &PathBuf) -> Result> { + pub async fn load_from_directory( + directory: &PathBuf, + ignore_transitive_versions: bool, + ) -> Result> { let modpack_lock_file_path = directory.clone().join(PathBuf::from(MODPACK_LOCK_FILENAME)); if !modpack_lock_file_path.exists() { let mut new_modpack_lock = Self::new(); new_modpack_lock - .init(&ModpackMeta::load_from_directory(directory)?) + .init( + &ModpackMeta::load_from_directory(directory)?, + ignore_transitive_versions, + ) .await?; return Ok(new_modpack_lock); }; @@ -223,7 +238,9 @@ impl PinnedPackMeta { Ok(toml::from_str(&modpack_lock_contents)?) } - pub async fn load_from_current_directory() -> Result> { - Self::load_from_directory(&std::env::current_dir()?).await + pub async fn load_from_current_directory( + ignore_transitive_versions: bool, + ) -> Result> { + Self::load_from_directory(&std::env::current_dir()?, ignore_transitive_versions).await } }