use crate::{config::Cfg, modrinth::{projects, Project, versions, extract_current_version, Version}, get_current_list, db::{userlist_get_all_ids, mods_get_versions, userlist_get_applicable_versions, userlist_change_versions, lists_get_all_ids, lists_get, userlist_get_current_version, mods_change_versions}, List, input::Input, files::{delete_version, download_versions, disable_version, clean_list_dir}, error::{MLE, MLError, ErrorType}};
pub async fn update(config: Cfg, input: Input) -> MLE<()> {
let mut liststack: Vec<List> = vec![];
if input.all_lists {
let list_ids = lists_get_all_ids(config.clone())?;
for id in list_ids {
liststack.push(lists_get(config.clone(), id)?);
}
} else {
let current = get_current_list(config.clone())?;
println!("Check for updates of mods in list {}", current.id);
liststack.push(current)
}
cmd_update(config, liststack, input.clean, input.direct_download, input.delete_old).await
}
pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_download: bool, delete_old: bool) -> MLE<()> {
for current_list in liststack {
let mods = userlist_get_all_ids(config.clone(), current_list.clone().id)?;
let mut current_versions: Vec<(String, String)> = vec![];
let mut versions = mods_get_versions(config.clone(), mods.clone())?;
versions.sort_by_key(|ver| ver.mod_id.clone());
let mut projects = projects(String::from(&config.apis.modrinth), mods).await;
projects.sort_by_key(|pro| pro.id.clone());
println!("Comparing mod versions:");
let mut updatestack: Vec<Version> = vec![];
for (index, project) in projects.into_iter().enumerate() {
//Get versions for project and check if they match up
let current_version = &versions[index];
let p_id = String::from(&project.id);
let v_id = ¤t_version.mod_id;
if &p_id != v_id { return Err(MLError::new(ErrorType::Other, "SORTING_ERROR")) };
println!("\t({}) Check for update", project.title);
//Getting current installed version for disable or delete
let disable_version = userlist_get_current_version(config.clone(), String::from(¤t_list.id), String::from(&project.id))?;
let version_db_string = project.versions.join("|");
//Adding to stack if not the same versions in the list OR if clean == true
if clean || (version_db_string != current_version.versions) {
updatestack.push(match specific_update(config.clone(), clean, current_list.clone(), project.clone()).await {
Ok(ver) => {
current_versions.push((disable_version, p_id));
ver
},
Err(e) => {
//Catch no update available
if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" {
mods_change_versions(config.clone(), version_db_string, project.id)?;
println!("\t └No new version found for the specified minecraft version");
} else {
return Err(e);
};
continue;
},
});
} else {
println!("\t └No new version found");
};
};
//Linebreak readability
println!("");
if clean { clean_list_dir(¤t_list)? };
//Linebreak readability
println!("");
if direct_download && !updatestack.is_empty() {
download_versions(current_list.clone(), config.clone(), updatestack).await?;
//Disable old versions
if !clean {
for ver in current_versions {
if delete_old {
println!("Deleting version {} for mod {}", ver.0, ver.1);
delete_version(current_list.clone(), ver.0)?;
} else if ver.0 != "NONE" {
println!("Disabling version {} for mod {}", ver.0, ver.1);
disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?;
};
}
}
};
}
Ok(())
}
async fn specific_update(config: Cfg, clean: bool, list: List, project: Project) -> MLE<Version> {
let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await;
let mut versions: Vec<String> = vec![];
if !applicable_versions.is_empty() {
for ver in &applicable_versions {
versions.push(String::from(&ver.id));
}
} else {
versions.push(String::from("NONE"));
}
let mut current: Vec<Version> = vec![];
if clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&project.id))?) {
//get new versions
println!("\t └Get versions for specified minecraft versions");
let current_str = extract_current_version(applicable_versions.clone())?;
let current_ver = match applicable_versions.into_iter().find(|ver| ver.id == current_str).ok_or("!no current version in applicable_versions") {
Ok(v) => Ok(v),
Err(e) => Err(MLError::new(ErrorType::Other, e)),
}?;
current.push(current_ver.clone());
let link = match current_ver.files.into_iter().find(|f| f.primary).ok_or("!no primary in links") {
Ok(p) => Ok(p),
Err(e) => Err(MLError::new(ErrorType::Other, e)),
}?.url;
userlist_change_versions(config, list.id, current_str, versions.join("|"), link, project.id)?;
}
if current.is_empty() { return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE")) };
//println!(" └✔️");
Ok(current[0].clone())
}
#[tokio::test]
async fn download_updates_test() {
use crate::{modrinth::{Version, VersionFile, Hash, VersionType}, Modloader, List};
let config = Cfg::init("modlist.toml").unwrap();
let current_list = List { id: String::from("..."), mc_version: String::from("..."), modloader: Modloader::Forge, download_folder: String::from("./dl") };
let versions = vec![Version {
id: "dEqtGnT9".to_string(),
project_id: "kYuIpRLv".to_string(),
author_id: "Qnt13hO8".to_string(),
featured: true,
name: "1.2.2-1.19 - Fabric".to_string(),
version_number: "1.2.2-1.19".to_string(),
changelog: None,
date_published: "2022-11-02T17:41:43.072267Z".to_string(),
downloads: 58,
version_type: VersionType::release,
files: vec![VersionFile {
hashes: Hash {
sha1: "fdc6dc39427fc92cc1d7ad8b275b5b83325e712b".to_string(),
sha512: "5b372f00d6e5d6a5ef225c3897826b9f6a2be5506905f7f71b9e939779765b41be6f2a9b029cfc752ad0751d0d2d5f8bb4544408df1363eebdde15641e99a849".to_string()
},
url: "https://cdn.modrinth.com/data/kYuIpRLv/versions/dEqtGnT9/waveycapes-fabric-1.2.2-mc1.19.2.jar".to_string(),
filename: "waveycapes-fabric-1.2.2-mc1.19.2.jar".to_string(),
primary: true,
size: 323176
}],
game_versions: vec![
"1.19".to_string(),
"1.19.1".to_string(),
"1.19.2".to_string()
],
loaders: vec![
"fabric".to_string()
]
}];
assert!(download_versions(current_list, config, versions).await.is_ok())
}