Skip to content

Commit 9c15972

Browse files
Fix pinning for GitHub release assets
1 parent 048951e commit 9c15972

9 files changed

Lines changed: 141 additions & 96 deletions

File tree

libium/src/add.rs

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ struct ReleaseAssetConnection {
8484
}
8585
#[derive(Deserialize, Debug)]
8686
struct ReleaseAsset {
87-
id: i64,
87+
id: String,
8888
name: String,
8989
}
9090

9191
pub fn parse_id(id: String) -> Result<ModIdentifier> {
92-
let split = id.split('-').collect_vec();
92+
let split = id.split(':').collect_vec();
9393
let (id, pin) = match split.as_slice() {
9494
[id, pin] => (id, Some(pin)),
9595
[id] => (id, None),
@@ -438,81 +438,81 @@ pub async fn add(
438438
);
439439

440440
for (((owner, repo), releases), pin) in gh_repos.into_iter().zip(gh_asset_ids) {
441-
let res = 'gh_check: {
442-
let identifier = ModIdentifier::GitHubRepository((owner.clone(), repo.clone()), pin);
443-
444-
if profile.mods.iter().any(|mod_| {
445-
mod_.name.eq_ignore_ascii_case(repo.as_ref())
446-
|| matches!(
447-
&mod_.identifier,
448-
ModIdentifier::GitHubRepository((o, r), _) if o == &owner && r == &repo,
449-
)
450-
}) {
451-
break 'gh_check Err(Error::AlreadyAdded);
452-
}
453-
if let Some(pin) = pin {
454-
if !releases
455-
.into_iter()
456-
.flat_map(|release| release.release_assets.nodes)
457-
.any(|asset| asset.id == pin)
458-
{
459-
break 'gh_check Err(Error::IncorrectVersionPin);
441+
let res =
442+
'gh_check: {
443+
let identifier =
444+
ModIdentifier::GitHubRepository((owner.clone(), repo.clone()), pin.clone());
445+
446+
if profile.mods.iter().any(|mod_| {
447+
mod_.name.eq_ignore_ascii_case(repo.as_ref())
448+
|| matches!(
449+
&mod_.identifier,
450+
ModIdentifier::GitHubRepository((o, r), _) if o == &owner && r == &repo,
451+
)
452+
}) {
453+
break 'gh_check Err(Error::AlreadyAdded);
460454
}
461-
} else if perform_checks {
462-
// Check if the repo is compatible
463-
check::select_latest(
464-
releases
455+
if let Some(pin) = &pin {
456+
if !releases
465457
.into_iter()
466-
.flat_map(|release| {
467-
release
468-
.release_assets
469-
.nodes
470-
.into_iter()
471-
.map(move |asset| Metadata {
472-
title: release.name.clone(),
473-
description: release.description.clone(),
474-
channel: if release.is_prerelease {
475-
ReleaseChannel::Beta
476-
} else {
477-
ReleaseChannel::Release
478-
},
479-
game_versions: asset
480-
.name
481-
.trim_end_matches(".jar")
482-
.split(['-', '_', '+'])
483-
.map(|s| s.trim_start_matches("mc"))
484-
.map(ToOwned::to_owned)
485-
.collect_vec(),
486-
loaders: asset
487-
.name
488-
.trim_end_matches(".jar")
489-
.split(['-', '_', '+'])
490-
.filter_map(|s| ModLoader::from_str(s).ok())
491-
.collect_vec(),
492-
filename: asset.name,
458+
.flat_map(|release| release.release_assets.nodes)
459+
.any(|asset| &asset.id == pin)
460+
{
461+
break 'gh_check Err(Error::IncorrectVersionPin);
462+
}
463+
} else if perform_checks {
464+
// Check if the repo is compatible
465+
check::select_latest(
466+
releases
467+
.into_iter()
468+
.flat_map(|release| {
469+
release.release_assets.nodes.into_iter().map(move |asset| {
470+
Metadata {
471+
title: release.name.clone(),
472+
description: release.description.clone(),
473+
channel: if release.is_prerelease {
474+
ReleaseChannel::Beta
475+
} else {
476+
ReleaseChannel::Release
477+
},
478+
game_versions: asset
479+
.name
480+
.trim_end_matches(".jar")
481+
.split(['-', '_', '+'])
482+
.map(|s| s.trim_start_matches("mc"))
483+
.map(ToOwned::to_owned)
484+
.collect_vec(),
485+
loaders: asset
486+
.name
487+
.trim_end_matches(".jar")
488+
.split(['-', '_', '+'])
489+
.filter_map(|s| ModLoader::from_str(s).ok())
490+
.collect_vec(),
491+
filename: asset.name,
492+
}
493493
})
494-
})
495-
.collect_vec()
496-
.iter(),
497-
if override_profile {
498-
profile.filters.clone()
499-
} else {
500-
[profile.filters.clone(), filters.clone()].concat()
501-
},
502-
)
503-
.await?;
504-
}
494+
})
495+
.collect_vec()
496+
.iter(),
497+
if override_profile {
498+
profile.filters.clone()
499+
} else {
500+
[profile.filters.clone(), filters.clone()].concat()
501+
},
502+
)
503+
.await?;
504+
}
505505

506-
profile.push_mod(
507-
repo.clone(),
508-
identifier.clone(),
509-
repo.clone(),
510-
override_profile,
511-
filters.clone(),
512-
);
506+
profile.push_mod(
507+
repo.clone(),
508+
identifier.clone(),
509+
repo.clone(),
510+
override_profile,
511+
filters.clone(),
512+
);
513513

514-
Ok(identifier)
515-
};
514+
Ok(identifier)
515+
};
516516
match res {
517517
Ok(id) => success_names.push((format!("{owner}/{repo}"), id)),
518518
Err(err) => errors.push((format!("{owner}/{repo}"), err)),

libium/src/config/mod.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@ use std::{
88

99
/// Open the config file at `path` and deserialise it into a config struct
1010
pub fn read_config(path: impl AsRef<Path>) -> Result<structs::Config> {
11-
if !path.as_ref().exists() {
12-
create_dir_all(path.as_ref().parent().expect("Invalid config directory"))?;
13-
write_config(&path, &structs::Config::default())?;
14-
}
11+
let path = match path.as_ref().canonicalize() {
12+
Ok(path) => path,
13+
Err(_) => {
14+
create_dir_all(path.as_ref().parent().expect("Invalid config directory"))?;
15+
write_config(&path, &structs::Config::default())?;
16+
path.as_ref().to_owned()
17+
}
18+
};
1519

1620
let config_file = BufReader::new(File::open(&path)?);
1721
let mut config: structs::Config = serde_json::from_reader(config_file)?;

libium/src/config/structs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,15 @@ pub enum ConfigModIdentifier {
182182

183183
PinnedCurseForgeProject(i32, i32),
184184
PinnedModrinthProject(String, String),
185-
PinnedGitHubRepository((String, String), i64),
185+
PinnedGitHubRepository((String, String), String),
186186
}
187187

188188
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
189189
#[serde(from = "ConfigModIdentifier", into = "ConfigModIdentifier")]
190190
pub enum ModIdentifier {
191191
CurseForgeProject(i32, Option<i32>),
192192
ModrinthProject(String, Option<String>),
193-
GitHubRepository((String, String), Option<i64>),
193+
GitHubRepository((String, String), Option<String>),
194194
}
195195

196196
impl From<ConfigModIdentifier> for ModIdentifier {

libium/src/upgrade/mod_downloadable.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub enum Error {
1818
DistributionDenied(#[from] DistributionDeniedError),
1919
CheckError(#[from] super::check::Error),
2020
#[error("The pin provided is an invalid identifier")]
21-
InvalidPinID(#[from] std::num::ParseIntError),
21+
InvalidPinID,
2222
#[error("Modrinth: {0}")]
2323
ModrinthError(#[from] ferinth::Error),
2424
#[error("CurseForge: {0}")]
@@ -40,9 +40,15 @@ impl Mod {
4040
ModIdentifier::GitHubRepository((owner, repo), Some(pin)) => Ok(from_gh_asset(
4141
GITHUB_API
4242
.repos(owner, repo)
43-
.release_assets()
44-
.get(*pin as u64)
45-
.await?,
43+
.releases()
44+
.list()
45+
.send()
46+
.await?
47+
.items
48+
.into_iter()
49+
.flat_map(|release| release.assets)
50+
.find(|asset| &asset.node_id == pin)
51+
.ok_or(Error::InvalidPinID)?,
4652
)),
4753
id => {
4854
let download_files = match &id {

src/download.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub async fn clean(
3232
println!(
3333
"{}",
3434
format!(
35-
"Warning: {} duplicate files were found {}. Remove the mod it belongs to",
35+
"WARNING: {} duplicate files were found {}. Remove the mod it belongs to",
3636
dupes.len(),
3737
dupes
3838
.into_iter()

src/subcommands/list.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,14 @@ pub fn curseforge(project: &Mod) {
168168
.iter()
169169
.map(|author| &author.name)
170170
.display(", ")
171-
.to_string()
171+
.clone()
172172
.cyan(),
173173
project
174174
.categories
175175
.iter()
176176
.map(|category| &category.name)
177177
.display(", ")
178-
.to_string()
178+
.clone()
179179
.magenta(),
180180
);
181181
}
@@ -208,14 +208,9 @@ pub fn modrinth(project: &Project, team_members: &[TeamMember]) {
208208
.iter()
209209
.map(|member| &member.user.username)
210210
.display(", ")
211-
.to_string()
211+
.clone()
212212
.cyan(),
213-
project
214-
.categories
215-
.iter()
216-
.display(", ")
217-
.to_string()
218-
.magenta(),
213+
project.categories.iter().display(", ").clone().magenta(),
219214
{
220215
if project.license.name.is_empty() {
221216
"Custom"
@@ -271,7 +266,7 @@ pub fn github(repo: &Repository, releases: &[Release]) {
271266
repo.topics.as_ref().map_or("".into(), |topics| topics
272267
.iter()
273268
.display(", ")
274-
.to_string()
269+
.clone()
275270
.magenta()),
276271
repo.license
277272
.as_ref()

src/subcommands/profile/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pub async fn check_output_directory(output_dir: &PathBuf) -> Result<()> {
8787
"The provided output directory is not absolute, i.e. it is a relative path"
8888
);
8989
if output_dir.file_name() != Some(std::ffi::OsStr::new("mods")) {
90-
println!("{}", "Warning! The output directory is not called `mods`. Most mod loaders will load from a directory called `mods`.".bright_yellow());
90+
println!("{}", "WARNING: The output directory is not called `mods`. Most mod loaders will load from a directory called `mods`.".bright_yellow());
9191
}
9292

9393
let mut backup = false;

src/subcommands/remove.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ pub fn remove(profile: &mut Profile, to_remove: Vec<String>) -> Result<()> {
3030
format!("{owner}/{repo}"),
3131
},
3232
match &mod_.identifier {
33-
ModIdentifier::ModrinthProject(_, Some(pin)) => format!(" (📌 {pin})"),
3433
ModIdentifier::CurseForgeProject(_, Some(pin)) => format!(" (📌 {pin})"),
35-
ModIdentifier::GitHubRepository(_, Some(pin)) => format!(" (📌 {pin})"),
34+
ModIdentifier::ModrinthProject(_, Some(pin))
35+
| ModIdentifier::GitHubRepository(_, Some(pin)) => format!(" (📌 {pin})"),
3636
_ => String::new(),
3737
},
3838
)

src/tests.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,46 @@ async fn already_added() {
233233
);
234234
}
235235

236+
#[tokio::test(flavor = "multi_thread")]
237+
async fn add_all_pinned() {
238+
assert_matches!(
239+
actual_main(get_args(
240+
SubCommands::Add {
241+
identifiers: vec![
242+
"starlight:HZYU0kdg".to_owned(),
243+
"591388:6713391".to_owned(),
244+
"CaffeineMC/sodium:RA_kwDODijHac4Kh-Lc".to_owned()
245+
],
246+
force: false,
247+
filters: FilterArguments::default(),
248+
},
249+
Some("empty_profile"),
250+
))
251+
.await,
252+
Ok(()),
253+
);
254+
}
255+
256+
#[tokio::test(flavor = "multi_thread")]
257+
async fn add_all_invalid_pins() {
258+
assert_matches!(
259+
actual_main(get_args(
260+
SubCommands::Add {
261+
identifiers: vec![
262+
"starlight:ihzX2Dvy".to_owned(),
263+
"591388:4947005".to_owned(),
264+
"CaffeineMC/sodium:kwDODijHac4Kh".to_owned()
265+
],
266+
force: false,
267+
filters: FilterArguments::default(),
268+
},
269+
Some("empty_profile"),
270+
))
271+
.await,
272+
Err(_),
273+
);
274+
}
275+
236276
#[tokio::test(flavor = "multi_thread")]
237277
async fn scan() {
238278
assert_matches!(

0 commit comments

Comments
 (0)