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}, error::{MLE, MLError, ErrorType}}; pub async fn update(config: Cfg, input: Input) -> MLE<()> { let mut liststack: Vec = 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!("Checking for updates of mods in {}", 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, 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()); let mut updatestack: Vec = 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")) }; //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 }, //TODO handle errors (only continue on "NO_UPDATE_AVAILABLE") Err(..) => { //Updating versions in modlist for no repeating version calls mods_change_versions(config.clone(), version_db_string, project.id)?; println!("({}) No new version found for the specified", project.title); continue; }, }); } else { println!("({}) No new version found", project.title); }; }; if clean { let dl_path = ¤t_list.download_folder; println!("Cleaning {}", dl_path); for entry in std::fs::read_dir(dl_path)? { let entry = entry?; std::fs::remove_file(entry.path())?; } } if direct_download { download_versions(current_list.clone(), updatestack).await?; //Disable old versions 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 { println!("Checking update for '{}' in {}", project.title, list.id); let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await; let mut versions: Vec = 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 = vec![]; if clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&project.id))?) { //get new versions print!(" | getting new version"); 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 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, versions).await.is_ok()) }