summaryrefslogblamecommitdiff
path: root/src/commands/update.rs
blob: 482e588d775cc56bd7d6a7fae4582d0453650d6f (plain) (tree)
1
2
3
4
5
6
                                                             
 
                                                                                                                                                                                                                                                                                                                                                                                                                                  
 
                                                                                          
 






                                                           


                                                                   
     
 

                                                                                  

                                                                 
 

                                                                            
 

                                                                                     
 






                                                                                                     




                                                                                                                                           

                                                                                      
                                                                                       
                                                                                                                                    



                                                                       
                                                                                 





                                                                                                                        



                                                                     
          







                                                        
         
        



                                                                                                  





                                                                                
         
     



          
                                                                                                                                  
                                                                       
    


                                                                                                                           






                                                 
     
 
    
                                           
                                                                                                                                                    


                                                                                




                                                                                                       
     
 




                                                                                                           
 



                                                                                                                              
 
                            
 



                                                                                
 


                                  
                                                                                      
    
                                                                                                                                                              






























                                                                                                                                                                              
                                                                    
 
use std::{io::{Error, ErrorKind}, fs::{rename, remove_file}};

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, userlist_add_disabled_versions, mods_change_versions}, List, input::Input, files::{get_file_path, delete_version}, download_versions};

pub async fn update(config: Cfg, input: Input) -> Result<(), Box<dyn std::error::Error>> {

    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!("Checking for updates of mods in {}", current.id);
        liststack.push(current)
    }

    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<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 = &current_version.mod_id;
            if &p_id != v_id { return Err(Box::new(Error::new(ErrorKind::Other, "SORTING_ERROR"))) };
            
            //Getting current installed version for disable or delete
            let disable_version = userlist_get_current_version(config.clone(), String::from(&current_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 input.clone().clean || (version_db_string != current_version.versions) {
                updatestack.push(match specific_update(config.clone(), input.clone(), 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(e) => {
                        //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 minecraft version({})", project.title, e);
                        continue;
                    },
                });
            } else {
                println!("({}) No new version found", project.title);
            };
        };

        if input.clean {
            let dl_path = &current_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 input.direct_download { download_versions(current_list.clone(), updatestack).await?; };

        //Disable old versions
        for ver in current_versions {
            if input.delete_old {
                println!("Deleting version {} for mod {}", ver.0, ver.1);
                delete_version(current_list.clone(), ver.0)?;
            } else { 
                disable_old(config.clone(), current_list.clone(), ver.0, ver.1)?
            };
        }
    }

    Ok(())
}

async fn specific_update(config: Cfg, input: Input, list: List, project: Project) -> Result<Version, Box<dyn std::error::Error>> {
    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<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 input.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 = applicable_versions.into_iter().find(|ver| ver.id == current_str).ok_or("")?;
        current.push(current_ver.clone());

        let link = current_ver.files.into_iter().find(|f| f.primary).ok_or("")?.url;
        userlist_change_versions(config, list.id, current_str, versions.join("|"), link, project.id)?;
    }

    if current.is_empty() { return Err(Box::new(Error::new(ErrorKind::NotFound, "NO_UPDATE_AVAILABLE"))) };
    
    println!(" | ✔️");
    Ok(current[0].clone())
}

fn disable_old(config: Cfg, current_list: List, versionid: String, mod_id: String) -> Result<(), Box<dyn std::error::Error>> {
    println!("Disabling version {} for mod {}", versionid, mod_id);
    let file = get_file_path(current_list.clone(), String::from(&versionid))?;
    let disabled = format!("{}.disabled", file);

    rename(file, disabled)?;

    userlist_add_disabled_versions(config, current_list.id, versionid, mod_id)?;

    Ok(())
}

#[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())
}