mirror of
				https://github.com/WarrenHood/MCModpackManager.git
				synced 2025-11-04 01:58:41 +00:00 
			
		
		
		
	Add support for ignoring transitive dep versions
This commit is contained in:
		
							parent
							
								
									6a18ac39bb
								
							
						
					
					
						commit
						86fa34d9a2
					
				
							
								
								
									
										14
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/main.rs
									
									
									
									
									
								
							| 
						 | 
					@ -59,6 +59,9 @@ enum Commands {
 | 
				
			||||||
        /// URL to download the mod from
 | 
					        /// URL to download the mod from
 | 
				
			||||||
        #[arg(long)]
 | 
					        #[arg(long)]
 | 
				
			||||||
        url: Option<String>,
 | 
					        url: Option<String>,
 | 
				
			||||||
 | 
					        /// Whether to ignore exact transitive mod versions
 | 
				
			||||||
 | 
					        #[arg(long, short, action)]
 | 
				
			||||||
 | 
					        ignore_transitive_versions: bool,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    /// Remove a mod from the modpack
 | 
					    /// Remove a mod from the modpack
 | 
				
			||||||
    Remove {
 | 
					    Remove {
 | 
				
			||||||
| 
						 | 
					@ -103,7 +106,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
 | 
				
			||||||
                    mc_modpack_meta = mc_modpack_meta.provider(provider);
 | 
					                    mc_modpack_meta = mc_modpack_meta.provider(provider);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                mc_modpack_meta.init_project(&dir)?;
 | 
					                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)?;
 | 
					                modpack_lock.save_to_dir(&dir)?;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Commands::New {
 | 
					            Commands::New {
 | 
				
			||||||
| 
						 | 
					@ -126,13 +129,14 @@ async fn main() -> Result<(), Box<dyn Error>> {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                mc_modpack_meta.init_project(&dir)?;
 | 
					                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)?;
 | 
					                modpack_lock.save_to_dir(&dir)?;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Commands::Add {
 | 
					            Commands::Add {
 | 
				
			||||||
                name,
 | 
					                name,
 | 
				
			||||||
                providers,
 | 
					                providers,
 | 
				
			||||||
                url,
 | 
					                url,
 | 
				
			||||||
 | 
					                ignore_transitive_versions,
 | 
				
			||||||
            } => {
 | 
					            } => {
 | 
				
			||||||
                let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
 | 
					                let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
 | 
				
			||||||
                let old_modpack_meta = modpack_meta.clone();
 | 
					                let old_modpack_meta = modpack_meta.clone();
 | 
				
			||||||
| 
						 | 
					@ -155,10 +159,10 @@ async fn main() -> Result<(), Box<dyn Error>> {
 | 
				
			||||||
                    panic!("Reverted modpack meta:\n{}", e);
 | 
					                    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) => {
 | 
					                    Ok(mut modpack_lock) => {
 | 
				
			||||||
                        let pin_result = 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;
 | 
					                            .await;
 | 
				
			||||||
                        if let Err(e) = pin_result {
 | 
					                        if let Err(e) = pin_result {
 | 
				
			||||||
                            revert_modpack_meta(e);
 | 
					                            revert_modpack_meta(e);
 | 
				
			||||||
| 
						 | 
					@ -188,7 +192,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
 | 
				
			||||||
                    panic!("Reverted modpack meta:\n{}", e);
 | 
					                    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) => {
 | 
					                    Ok(mut modpack_lock) => {
 | 
				
			||||||
                        let remove_result = modpack_lock.remove_mod(&name, &modpack_meta);
 | 
					                        let remove_result = modpack_lock.remove_mod(&name, &modpack_meta);
 | 
				
			||||||
                        if let Err(e) = remove_result {
 | 
					                        if let Err(e) = remove_result {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,7 @@ pub struct PinnedMod {
 | 
				
			||||||
    /// Source of the files for the mod
 | 
					    /// Source of the files for the mod
 | 
				
			||||||
    source: Vec<FileSource>,
 | 
					    source: Vec<FileSource>,
 | 
				
			||||||
    /// Version of mod
 | 
					    /// Version of mod
 | 
				
			||||||
    pub version: semver::Version,
 | 
					    pub version: String,
 | 
				
			||||||
    /// Pinned dependencies of a pinned mod
 | 
					    /// Pinned dependencies of a pinned mod
 | 
				
			||||||
    pub deps: Option<HashSet<ModMeta>>
 | 
					    pub deps: Option<HashSet<ModMeta>>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -64,16 +64,16 @@ struct VersionFiles {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Serialize, Deserialize, Debug)]
 | 
					#[derive(Serialize, Deserialize, Debug)]
 | 
				
			||||||
struct ModrinthProjectVersion {
 | 
					struct ModrinthProjectVersion {
 | 
				
			||||||
    author_id: String,
 | 
					    // author_id: String,
 | 
				
			||||||
    date_published: String,
 | 
					    // date_published: String,
 | 
				
			||||||
    dependencies: Vec<VersionDeps>,
 | 
					    dependencies: Option<Vec<VersionDeps>>,
 | 
				
			||||||
    // downloads: i64,
 | 
					    // downloads: i64,
 | 
				
			||||||
    files: Vec<VersionFiles>,
 | 
					    files: Vec<VersionFiles>,
 | 
				
			||||||
    // loaders: Vec<String>,
 | 
					    // loaders: Vec<String>,
 | 
				
			||||||
    name: String,
 | 
					    // name: String,
 | 
				
			||||||
    project_id: String,
 | 
					    // project_id: String,
 | 
				
			||||||
    id: String,
 | 
					    id: String,
 | 
				
			||||||
    version_number: semver::Version,
 | 
					    version_number: String,
 | 
				
			||||||
    // version_type: String,
 | 
					    // version_type: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,10 +102,12 @@ impl Modrinth {
 | 
				
			||||||
        project_version: Option<&str>,
 | 
					        project_version: Option<&str>,
 | 
				
			||||||
        pack_meta: &ModpackMeta,
 | 
					        pack_meta: &ModpackMeta,
 | 
				
			||||||
    ) -> Result<ModMeta, Box<dyn Error>> {
 | 
					    ) -> Result<ModMeta, Box<dyn Error>> {
 | 
				
			||||||
        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?;
 | 
					        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 {
 | 
					            if project_version.is_none() || project_version.unwrap_or("*") == version.id {
 | 
				
			||||||
                return Ok(ModMeta::new(&project_slug)?
 | 
					                return Ok(ModMeta::new(&project_slug)?
 | 
				
			||||||
                    .provider(ModProvider::Modrinth)
 | 
					                    .provider(ModProvider::Modrinth)
 | 
				
			||||||
| 
						 | 
					@ -126,20 +128,21 @@ impl Modrinth {
 | 
				
			||||||
        mod_meta: &ModMeta,
 | 
					        mod_meta: &ModMeta,
 | 
				
			||||||
        pack_meta: &ModpackMeta,
 | 
					        pack_meta: &ModpackMeta,
 | 
				
			||||||
    ) -> Result<PinnedMod, Box<dyn Error>> {
 | 
					    ) -> Result<PinnedMod, Box<dyn Error>> {
 | 
				
			||||||
        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 == "*" {
 | 
					        let package = if mod_meta.version == "*" {
 | 
				
			||||||
            versions.last().ok_or(format!(
 | 
					            versions.first().ok_or(format!(
 | 
				
			||||||
                "Cannot find package {} for loader={} and mc version={}",
 | 
					                "Cannot find package {} for loader={} and mc version={}",
 | 
				
			||||||
                mod_meta.name,
 | 
					                mod_meta.name,
 | 
				
			||||||
                pack_meta.modloader.to_string().to_lowercase(),
 | 
					                pack_meta.modloader.to_string().to_lowercase(),
 | 
				
			||||||
                pack_meta.mc_version
 | 
					                pack_meta.mc_version
 | 
				
			||||||
            ))?
 | 
					            ))?
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            let expected_version = semver::Version::parse(&mod_meta.version)?;
 | 
					 | 
				
			||||||
            versions
 | 
					            versions
 | 
				
			||||||
                .iter()
 | 
					                .iter()
 | 
				
			||||||
                .filter(|v| v.version_number == expected_version)
 | 
					                .filter(|v| v.version_number == mod_meta.version)
 | 
				
			||||||
                .nth(0)
 | 
					                .nth(0)
 | 
				
			||||||
                .ok_or(format!(
 | 
					                .ok_or(format!(
 | 
				
			||||||
                    "Cannot find package {}@{}",
 | 
					                    "Cannot find package {}@{}",
 | 
				
			||||||
| 
						 | 
					@ -148,15 +151,13 @@ impl Modrinth {
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut deps_meta = HashSet::new();
 | 
					        let mut deps_meta = HashSet::new();
 | 
				
			||||||
        for dep in package
 | 
					        if let Some(deps) = &package.dependencies {
 | 
				
			||||||
            .dependencies
 | 
					            for dep in deps.iter().filter(|dep| dep.dependency_type == "required") {
 | 
				
			||||||
            .iter()
 | 
					                deps_meta.insert(
 | 
				
			||||||
            .filter(|dep| dep.dependency_type == "required")
 | 
					                    self.get_mod_meta(&dep.project_id, dep.version_id.as_deref(), pack_meta)
 | 
				
			||||||
        {
 | 
					                        .await?,
 | 
				
			||||||
            deps_meta.insert(
 | 
					                );
 | 
				
			||||||
                self.get_mod_meta(&dep.project_id, dep.version_id.as_deref(), pack_meta)
 | 
					            }
 | 
				
			||||||
                    .await?,
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(PinnedMod {
 | 
					        Ok(PinnedMod {
 | 
				
			||||||
| 
						 | 
					@ -170,7 +171,11 @@ impl Modrinth {
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
                .collect(),
 | 
					                .collect(),
 | 
				
			||||||
            version: package.version_number.clone(),
 | 
					            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)
 | 
					                Some(deps_meta)
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                None
 | 
					                None
 | 
				
			||||||
| 
						 | 
					@ -182,19 +187,26 @@ impl Modrinth {
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        mod_id: &str,
 | 
					        mod_id: &str,
 | 
				
			||||||
        pack_meta: &ModpackMeta,
 | 
					        pack_meta: &ModpackMeta,
 | 
				
			||||||
 | 
					        ignore_game_version_and_loader: bool, // For deps we might as well let them use anything
 | 
				
			||||||
    ) -> Result<Vec<ModrinthProjectVersion>, Box<dyn Error>> {
 | 
					    ) -> Result<Vec<ModrinthProjectVersion>, Box<dyn Error>> {
 | 
				
			||||||
        let mut project_versions: Vec<ModrinthProjectVersion> = self
 | 
					        let query_vec = if ignore_game_version_and_loader {
 | 
				
			||||||
            .client
 | 
					            &vec![]
 | 
				
			||||||
            .get(format!(
 | 
					        } else {
 | 
				
			||||||
                "https://api.modrinth.com/v2/project/{mod_id}/version"
 | 
					            &vec![
 | 
				
			||||||
            ))
 | 
					 | 
				
			||||||
            .query(&[
 | 
					 | 
				
			||||||
                (
 | 
					                (
 | 
				
			||||||
                    "loaders",
 | 
					                    "loaders",
 | 
				
			||||||
                    format!("[\"{}\"]", pack_meta.modloader.to_string().to_lowercase()),
 | 
					                    format!("[\"{}\"]", pack_meta.modloader.to_string().to_lowercase()),
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
                ("game_versions", format!("[\"{}\"]", pack_meta.mc_version)),
 | 
					                ("game_versions", format!("[\"{}\"]", pack_meta.mc_version)),
 | 
				
			||||||
            ])
 | 
					            ]
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut project_versions: Vec<ModrinthProjectVersion> = self
 | 
				
			||||||
 | 
					            .client
 | 
				
			||||||
 | 
					            .get(format!(
 | 
				
			||||||
 | 
					                "https://api.modrinth.com/v2/project/{mod_id}/version"
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					            .query(query_vec)
 | 
				
			||||||
            .send()
 | 
					            .send()
 | 
				
			||||||
            .await?
 | 
					            .await?
 | 
				
			||||||
            .json()
 | 
					            .json()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,11 +32,10 @@ impl PinnedPackMeta {
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        mod_metadata: &ModMeta,
 | 
					        mod_metadata: &ModMeta,
 | 
				
			||||||
        pack_metadata: &ModpackMeta,
 | 
					        pack_metadata: &ModpackMeta,
 | 
				
			||||||
 | 
					        ignore_transitive_versions: bool,
 | 
				
			||||||
    ) -> Result<(), Box<dyn Error>> {
 | 
					    ) -> Result<(), Box<dyn Error>> {
 | 
				
			||||||
        if let Some(mod_meta) = self.mods.get(&mod_metadata.name) {
 | 
					        if let Some(mod_meta) = self.mods.get(&mod_metadata.name) {
 | 
				
			||||||
            if mod_metadata.version != "*"
 | 
					            if mod_metadata.version != "*" && mod_metadata.version == mod_meta.version {
 | 
				
			||||||
                && semver::Version::parse(&mod_metadata.version)? == mod_meta.version
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // Skip already pinned mods
 | 
					                // 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
 | 
					                // 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(());
 | 
					                return Ok(());
 | 
				
			||||||
| 
						 | 
					@ -45,6 +44,11 @@ impl PinnedPackMeta {
 | 
				
			||||||
        let mut deps =
 | 
					        let mut deps =
 | 
				
			||||||
            HashSet::from_iter(self.pin_mod(mod_metadata, pack_metadata).await?.into_iter());
 | 
					            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
 | 
					        let pinned_version = self
 | 
				
			||||||
            .mods
 | 
					            .mods
 | 
				
			||||||
            .get(&mod_metadata.name)
 | 
					            .get(&mod_metadata.name)
 | 
				
			||||||
| 
						 | 
					@ -183,9 +187,14 @@ impl PinnedPackMeta {
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn init(&mut self, modpack_meta: &ModpackMeta) -> Result<(), Box<dyn Error>> {
 | 
					    pub async fn init(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        modpack_meta: &ModpackMeta,
 | 
				
			||||||
 | 
					        ignore_transitive_versions: bool,
 | 
				
			||||||
 | 
					    ) -> Result<(), Box<dyn Error>> {
 | 
				
			||||||
        for mod_meta in modpack_meta.iter_mods() {
 | 
					        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(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -210,12 +219,18 @@ impl PinnedPackMeta {
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn load_from_directory(directory: &PathBuf) -> Result<Self, Box<dyn Error>> {
 | 
					    pub async fn load_from_directory(
 | 
				
			||||||
 | 
					        directory: &PathBuf,
 | 
				
			||||||
 | 
					        ignore_transitive_versions: bool,
 | 
				
			||||||
 | 
					    ) -> Result<Self, Box<dyn Error>> {
 | 
				
			||||||
        let modpack_lock_file_path = directory.clone().join(PathBuf::from(MODPACK_LOCK_FILENAME));
 | 
					        let modpack_lock_file_path = directory.clone().join(PathBuf::from(MODPACK_LOCK_FILENAME));
 | 
				
			||||||
        if !modpack_lock_file_path.exists() {
 | 
					        if !modpack_lock_file_path.exists() {
 | 
				
			||||||
            let mut new_modpack_lock = Self::new();
 | 
					            let mut new_modpack_lock = Self::new();
 | 
				
			||||||
            new_modpack_lock
 | 
					            new_modpack_lock
 | 
				
			||||||
                .init(&ModpackMeta::load_from_directory(directory)?)
 | 
					                .init(
 | 
				
			||||||
 | 
					                    &ModpackMeta::load_from_directory(directory)?,
 | 
				
			||||||
 | 
					                    ignore_transitive_versions,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
                .await?;
 | 
					                .await?;
 | 
				
			||||||
            return Ok(new_modpack_lock);
 | 
					            return Ok(new_modpack_lock);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
| 
						 | 
					@ -223,7 +238,9 @@ impl PinnedPackMeta {
 | 
				
			||||||
        Ok(toml::from_str(&modpack_lock_contents)?)
 | 
					        Ok(toml::from_str(&modpack_lock_contents)?)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn load_from_current_directory() -> Result<Self, Box<dyn Error>> {
 | 
					    pub async fn load_from_current_directory(
 | 
				
			||||||
        Self::load_from_directory(&std::env::current_dir()?).await
 | 
					        ignore_transitive_versions: bool,
 | 
				
			||||||
 | 
					    ) -> Result<Self, Box<dyn Error>> {
 | 
				
			||||||
 | 
					        Self::load_from_directory(&std::env::current_dir()?, ignore_transitive_versions).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue