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




                                                                                                                                                                                                            




                                                                            



                                                                         
 

                                                                                 
    


















                                                                                                         
                                                 





                                                                                                                    
    


                                                                                                                           
 


                                             
    







                                                                                                         
 




                                                                                                           
 
                                                                                                              
 
                                                  
 



                                                                                     
 








                                                                                                                               
 





                                                                      
 








































                                                                                                                                                                              
 
use std::{io::{Error, ErrorKind, Write}, fs::File};

use reqwest::Client;

use futures_util::StreamExt;

use crate::{config::Cfg, modrinth::{projects, Project, versions, extract_current_version, Version}, get_current_list, db::{get_mods_from_list, get_versions, get_list_version, change_list_versions}, List};

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

    let current_list = get_current_list(config.clone())?;

    let mods = get_mods_from_list(config.clone(), current_list.clone())?;

    let mut versions = 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() {
        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"))) };
        
        if project.versions.join("|") != current_version.versions {
            updatestack.push(match specific_update(config.clone(), current_list.clone(), project).await {
                Ok(ver) => ver,
                //TODO handle errors (only continue on "NO_UPDATE_AVAILABLE")
                Err(_) => { continue; },
            });
        };
    };
    //println!("{:?}", updatestack);

    download_updates(config, updatestack).await?;

    Ok(())
}

async fn specific_update(config: Cfg, list: List, project: Project) -> Result<Version, Box<dyn std::error::Error>> {
    print!("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![];

    for ver in &applicable_versions {
        versions.push(String::from(&ver.id));
    }
    
    let mut current: Vec<Version> = vec![];
    if versions.join("|") != get_list_version(config.clone(), list.clone(), String::from(&project.id))? {
        //get new versions
        print!(" | getting new version");
        let current_str = extract_current_version(applicable_versions.clone())?;
        current.push(applicable_versions.into_iter().find(|ver| ver.id == current_str).unwrap());
        change_list_versions(config, list, current_str, versions, project.id)?;
    }

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

async fn download_updates(config: Cfg, versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> {

    let dl_path = String::from(&config.downloads);

    for ver in versions {
        let primary_file = ver.files.into_iter().find(|file| file.primary).unwrap();
        let dl_path_file = format!("{}/{}", config.downloads, primary_file.filename);
        println!("Downloading {}", primary_file.url);

        let res = Client::new()
            .get(String::from(&primary_file.url))
            .send()
            .await
            .or(Err(format!("Failed to GET from '{}'", &primary_file.url)))?;
        
        // download chunks
        let mut file = File::create(String::from(&dl_path_file)).or(Err(format!("Failed to create file '{}'", dl_path_file)))?;
        let mut stream = res.bytes_stream();

        while let Some(item) = stream.next().await {
            let chunk = item.or(Err("Error while downloading file"))?;
            file.write_all(&chunk)
                .or(Err("Error while writing to file"))?;
        }
    }

    Ok(dl_path)
}

#[tokio::test]
async fn download_updates_test() {

    use crate::{modrinth::{Version, VersionFile, Hash, VersionType}, config::{Cfg, Apis}};
    
    let config = Cfg { data: "...".to_string(), clean_remove: false, downloads: "./dl".to_string(), apis: Apis { modrinth: "...".to_string() } };

    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_eq!(download_updates(config, versions).await.unwrap(), "./dl")
}