Automatically pin transitive deps

This commit is contained in:
Warren Hood 2024-08-17 23:34:06 +02:00
parent 844350ab09
commit 72407055ca
4 changed files with 115 additions and 24 deletions

View file

@ -80,15 +80,15 @@ impl ModpackMeta {
} }
pub fn add_mod(mut self, mod_meta: &ModMeta) -> Self { pub fn add_mod(mut self, mod_meta: &ModMeta) -> Self {
if let Some(old_mod_meta) = self.mods.get(&mod_meta.name) { // if let Some(old_mod_meta) = self.mods.get(&mod_meta.name) {
println!("Updating {} version constraints: {} -> {}", mod_meta.name, old_mod_meta.version, mod_meta.version); // println!("Updating {} version constraints: {} -> {}", mod_meta.name, old_mod_meta.version, mod_meta.version);
} // }
else { // else {
println!( // println!(
"Adding {}@{} to modpack '{}'...", // "Adding {}@{} to modpack '{}'...",
mod_meta.name, mod_meta.version, self.pack_name // mod_meta.name, mod_meta.version, self.pack_name
); // );
} // }
self.mods.insert(mod_meta.name.to_string(), mod_meta.clone()); self.mods.insert(mod_meta.name.to_string(), mod_meta.clone());
self self
} }
@ -113,7 +113,7 @@ impl ModpackMeta {
path, path,
toml::to_string(self).expect("MC Modpack Meta should be serializable"), toml::to_string(self).expect("MC Modpack Meta should be serializable"),
)?; )?;
println!("Saved modpack metadata to {}", path.display()); // println!("Saved modpack metadata to {}", path.display());
Ok(()) Ok(())
} }

View file

@ -1,4 +1,4 @@
use std::path::PathBuf; use std::{collections::HashSet, path::PathBuf};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::mod_meta::ModMeta; use crate::mod_meta::ModMeta;
@ -18,5 +18,5 @@ pub struct PinnedMod {
/// Version of mod /// Version of mod
pub version: semver::Version, pub version: semver::Version,
/// Pinned dependencies of a pinned mod /// Pinned dependencies of a pinned mod
pub deps: Option<Vec<ModMeta>> pub deps: Option<HashSet<ModMeta>>
} }

View file

@ -1,13 +1,43 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::error::Error; use std::{collections::HashSet, error::Error};
use super::PinnedMod; use super::PinnedMod;
use crate::{mod_meta::ModMeta, modpack::ModpackMeta, providers::FileSource}; use crate::{
mod_meta::{ModMeta, ModProvider},
modpack::ModpackMeta,
providers::FileSource,
};
pub struct Modrinth { pub struct Modrinth {
client: reqwest::Client, client: reqwest::Client,
} }
#[derive(Serialize, Deserialize)]
struct DonationUrls1 {
id: String,
platform: String,
url: String,
}
#[derive(Serialize, Deserialize)]
struct Gallery1 {
created: String,
description: String,
featured: bool,
ordering: i64,
title: String,
url: String,
}
#[derive(Serialize, Deserialize)]
struct License1 {
id: String,
name: String,
url: String,
}
#[derive(Serialize, Deserialize)]
struct ModrinthProject {
slug: String,
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
struct VersionDeps { struct VersionDeps {
dependency_type: String, dependency_type: String,
@ -40,8 +70,9 @@ struct ModrinthProjectVersion {
// 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,
version_number: semver::Version, version_number: semver::Version,
// version_type: String, // version_type: String,
} }
@ -53,15 +84,49 @@ impl Modrinth {
} }
} }
pub async fn get_project_slug(&self, project_id: &str) -> Result<String, Box<dyn Error>> {
let mut project: ModrinthProject = self
.client
.get(format!("https://api.modrinth.com/v2/project/{project_id}"))
.send()
.await?
.json()
.await?;
Ok(project.slug)
}
pub async fn get_mod_meta(
&self,
project_id: &str,
project_version: Option<&str>,
pack_meta: &ModpackMeta,
) -> Result<ModMeta, Box<dyn Error>> {
let project_versions = self.get_project_versions(project_id, pack_meta).await?;
let project_slug = self.get_project_slug(project_id).await?;
for version in project_versions.into_iter() {
if project_version.is_none() || project_version.unwrap_or("*") == version.id {
return Ok(ModMeta::new(&project_slug)?
.provider(ModProvider::Modrinth)
.version(&version.version_number.to_string()));
}
}
Err(format!(
"Couldn't find project '{}' with version '{}'",
project_id,
project_version.unwrap_or("*")
)
.into())
}
/// Resolve a list of mod candidates in order of newest to oldest /// Resolve a list of mod candidates in order of newest to oldest
pub async fn resolve( pub async fn resolve(
&self, &self,
mod_meta: &ModMeta, mod_meta: &ModMeta,
pack_meta: &ModpackMeta, pack_meta: &ModpackMeta,
) -> Result<PinnedMod, Box<dyn Error>> { ) -> Result<PinnedMod, Box<dyn Error>> {
let mut versions = self.get_project_versions(&mod_meta.name, pack_meta).await?; let versions = self.get_project_versions(&mod_meta.name, pack_meta).await?;
versions.sort_by_key(|v| v.version_number.clone());
versions.reverse();
let package = if mod_meta.version == "*" { let package = if mod_meta.version == "*" {
versions.last().ok_or(format!( versions.last().ok_or(format!(
@ -82,6 +147,18 @@ 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?,
);
}
Ok(PinnedMod { Ok(PinnedMod {
source: package source: package
.files .files
@ -93,7 +170,11 @@ impl Modrinth {
}) })
.collect(), .collect(),
version: package.version_number.clone(), version: package.version_number.clone(),
deps: None, // TODO: Get deps deps: if package.dependencies.len() > 0 {
Some(deps_meta)
} else {
None
},
}) })
} }
@ -102,7 +183,7 @@ impl Modrinth {
mod_id: &str, mod_id: &str,
pack_meta: &ModpackMeta, pack_meta: &ModpackMeta,
) -> Result<Vec<ModrinthProjectVersion>, Box<dyn Error>> { ) -> Result<Vec<ModrinthProjectVersion>, Box<dyn Error>> {
let project_Versions: Vec<ModrinthProjectVersion> = self let mut project_versions: Vec<ModrinthProjectVersion> = self
.client .client
.get(format!( .get(format!(
"https://api.modrinth.com/v2/project/{mod_id}/version" "https://api.modrinth.com/v2/project/{mod_id}/version"
@ -119,7 +200,10 @@ impl Modrinth {
.json() .json()
.await?; .await?;
Ok(project_Versions) project_versions.sort_by_key(|v| v.version_number.clone());
project_versions.reverse();
Ok(project_versions)
} }
} }

View file

@ -45,12 +45,19 @@ 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());
let pinned_version = self
.mods
.get(&mod_metadata.name)
.expect("should be in pinned mods")
.version
.clone();
while !deps.is_empty() { while !deps.is_empty() {
let mut next_deps = HashSet::new(); let mut next_deps = HashSet::new();
for dep in deps.iter() { for dep in deps.iter() {
println!( println!(
"Adding mod {}@{} (dependency of {}@{})", "Adding mod {}@{} (dependency of {}@{})",
dep.name, dep.version, mod_metadata.name, mod_metadata.version dep.name, dep.version, mod_metadata.name, pinned_version
); );
next_deps.extend(self.pin_mod(dep, &pack_metadata).await?); next_deps.extend(self.pin_mod(dep, &pack_metadata).await?);
} }
@ -129,7 +136,7 @@ impl PinnedPackMeta {
path, path,
toml::to_string(self).expect("Pinned pack meta should be serializable"), toml::to_string(self).expect("Pinned pack meta should be serializable"),
)?; )?;
println!("Saved modpack.lock to {}", path.display()); // println!("Saved modpack.lock to {}", path.display());
Ok(()) Ok(())
} }