summaryrefslogtreecommitdiff
path: root/src/commands
diff options
context:
space:
mode:
authorfx <[email protected]>2023-05-29 18:02:08 +0200
committerfx <[email protected]>2023-05-29 18:02:08 +0200
commitd3870a2efa74e68c643dfb4aef32edc2536503b0 (patch)
tree116075aaa57c35afca2749719d450c3cb473ab3e /src/commands
parent5a2ea0755b29a8811aeeec1c73679c5783082628 (diff)
parentc7ecf3019a75dc0ab1a0aefeb9b880899fc8a231 (diff)
downloadmodlist-d3870a2efa74e68c643dfb4aef32edc2536503b0.tar
modlist-d3870a2efa74e68c643dfb4aef32edc2536503b0.tar.gz
modlist-d3870a2efa74e68c643dfb4aef32edc2536503b0.zip
Merge pull request 'multithreaded' (#6) from multithreaded into master
Reviewed-on: http://raspberrypi.fritz.box:7920/fx/modlist/pulls/6
Diffstat (limited to 'src/commands')
-rw-r--r--src/commands/download.rs93
-rw-r--r--src/commands/io.rs72
-rw-r--r--src/commands/list.rs85
-rw-r--r--src/commands/modification.rs204
-rw-r--r--src/commands/update.rs222
5 files changed, 430 insertions, 246 deletions
diff --git a/src/commands/download.rs b/src/commands/download.rs
index ebfb4eb..a7cf744 100644
--- a/src/commands/download.rs
+++ b/src/commands/download.rs
@@ -1,26 +1,48 @@
1use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
2
1use crate::{config::Cfg, List}; 3use crate::{config::Cfg, List};
2use crate::{ 4use crate::{
3 db::userlist_get_all_current_versions_with_mods, 5 db::userlist_get_all_current_versions_with_mods,
4 error::{ErrorType, MLError, MLE}, 6 error::{ErrorType, MLError, MLE},
5 files::{ 7 files::{
6 clean_list_dir, delete_version, disable_version, download_versions, get_downloaded_versions, 8 clean_list_dir, delete_version, disable_version, download_versions,
9 get_downloaded_versions,
7 }, 10 },
8 modrinth::get_raw_versions, 11 modrinth::get_raw_versions,
9}; 12};
13use crate::{PROGRESS_CHARS, STYLE_BAR_POS};
10 14
11pub async fn download(config: Cfg, liststack: Vec<List>, clean: bool, delete_old: bool) -> MLE<()> { 15pub async fn download(
16 config: &Cfg,
17 liststack: Vec<List>,
18 clean: bool,
19 delete_old: bool,
20) -> MLE<()> {
21 let mp = MultiProgress::new();
22 let download_p =
23 mp.add(ProgressBar::new(liststack.len().try_into().unwrap()));
24 download_p.set_style(
25 ProgressStyle::with_template(STYLE_BAR_POS)
26 .unwrap()
27 .progress_chars(PROGRESS_CHARS),
28 );
12 29
13 for current_list in liststack { 30 for current_list in liststack {
14 println!("Downloading current versions of mods in {}", current_list.id); 31 download_p.set_message(format!("Download in {}", current_list.id));
15 let downloaded_versions = get_downloaded_versions(current_list.clone())?; 32
16 // println!("To download: {:#?}", downloaded_versions); 33 let downloaded_versions =
17 let current_version_ids = match userlist_get_all_current_versions_with_mods( 34 get_downloaded_versions(current_list.clone())?;
18 config.clone(), 35 let current_version_ids =
19 String::from(&current_list.id), 36 match userlist_get_all_current_versions_with_mods(
20 ) { 37 config,
21 Ok(i) => Ok(i), 38 String::from(&current_list.id),
22 Err(e) => Err(MLError::new(ErrorType::DBError, e.to_string().as_str())), 39 ) {
23 }?; 40 Ok(i) => Ok(i),
41 Err(e) => Err(MLError::new(
42 ErrorType::DBError,
43 e.to_string().as_str(),
44 )),
45 }?;
24 46
25 let mut to_download: Vec<String> = vec![]; 47 let mut to_download: Vec<String> = vec![];
26 //(mod_id, version_id) 48 //(mod_id, version_id)
@@ -39,7 +61,10 @@ pub async fn download(config: Cfg, liststack: Vec<List>, clean: bool, delete_old
39 .ok_or("SOMETHING_HAS_REALLY_GONE_WRONG") 61 .ok_or("SOMETHING_HAS_REALLY_GONE_WRONG")
40 .unwrap(); 62 .unwrap();
41 if &current_version != downloaded_version { 63 if &current_version != downloaded_version {
42 to_disable.push((mod_id.clone(), String::from(downloaded_version))); 64 to_disable.push((
65 mod_id.clone(),
66 String::from(downloaded_version),
67 ));
43 to_download.push(current_version); 68 to_download.push(current_version);
44 } 69 }
45 } 70 }
@@ -54,23 +79,57 @@ pub async fn download(config: Cfg, liststack: Vec<List>, clean: bool, delete_old
54 current_list.clone(), 79 current_list.clone(),
55 config.clone(), 80 config.clone(),
56 get_raw_versions(&config.apis.modrinth, to_download).await, 81 get_raw_versions(&config.apis.modrinth, to_download).await,
82 &mp,
83 &download_p,
57 ) 84 )
58 .await?; 85 .await?;
59 } else { 86 } else {
60 println!("There are no new versions to download"); 87 download_p.println(format!(
88 "There are no new versions to download for {}",
89 current_list.id
90 ));
61 } 91 }
62 92
63 if !to_disable.is_empty() { 93 if !to_disable.is_empty() {
94 let d_p = mp.insert_before(
95 &download_p,
96 ProgressBar::new(to_disable.len().try_into().unwrap()),
97 );
98 d_p.set_style(
99 ProgressStyle::with_template(STYLE_BAR_POS)
100 .unwrap()
101 .progress_chars(PROGRESS_CHARS),
102 );
64 for ver in to_disable { 103 for ver in to_disable {
65 if delete_old { 104 if delete_old {
66 println!("Deleting version {} for mod {}", ver.1, ver.0); 105 d_p.set_message(format!("Delete version {}", ver.1));
67 delete_version(current_list.clone(), ver.1)?; 106 d_p.inc(1);
107 delete_version(&current_list, ver.1)?;
68 } else { 108 } else {
69 disable_version(config.clone(), current_list.clone(), ver.1, ver.0)?; 109 d_p.set_message(format!("Disable version {}", ver.1));
110 d_p.inc(1);
111 disable_version(
112 config,
113 current_list.clone(),
114 ver.1,
115 ver.0,
116 )?;
70 }; 117 };
71 } 118 }
119
120 let del_msg = if delete_old {
121 "Deleted all old versions"
122 } else {
123 "Disabled all old versions"
124 };
125
126 d_p.finish_with_message(del_msg);
72 } 127 }
128
129 download_p.inc(1);
73 } 130 }
74 131
132 download_p.finish_with_message("Downloaded all lists");
133
75 Ok(()) 134 Ok(())
76} 135}
diff --git a/src/commands/io.rs b/src/commands/io.rs
index dd294bc..8e44b2b 100644
--- a/src/commands/io.rs
+++ b/src/commands/io.rs
@@ -1,12 +1,16 @@
1use indicatif::{ProgressBar, ProgressStyle};
1use serde::{Deserialize, Serialize}; 2use serde::{Deserialize, Serialize};
2use std::fs::File; 3use std::fs::File;
3use std::io::prelude::*; 4use std::io::prelude::*;
4 5
5use crate::{ 6use crate::{
6 config::Cfg, 7 config::Cfg,
7 db::{lists_get, lists_get_all_ids, lists_insert, userlist_get_set_version, userlist_get_all_ids, userlist_get_current_version}, 8 db::{
9 lists_get, lists_get_all_ids, lists_insert, userlist_get_all_ids,
10 userlist_get_current_version, userlist_get_set_version,
11 },
8 error::MLE, 12 error::MLE,
9 mod_add, IDSelector, List, Modloader, AddMod, 13 mod_add, AddMod, IDSelector, List, Modloader, STYLE_OPERATION,
10}; 14};
11 15
12#[derive(Debug, Serialize, Deserialize)] 16#[derive(Debug, Serialize, Deserialize)]
@@ -17,14 +21,14 @@ struct Export {
17#[derive(Debug, Serialize, Deserialize)] 21#[derive(Debug, Serialize, Deserialize)]
18struct ExportVersion { 22struct ExportVersion {
19 version: String, 23 version: String,
20 set: bool 24 set: bool,
21} 25}
22 26
23impl ExportVersion { 27impl ExportVersion {
24 fn from(config: Cfg, list_id: &str, mod_id: &str) -> MLE<Self> { 28 fn from(config: &Cfg, list_id: &str, mod_id: &str) -> MLE<Self> {
25 Ok(Self { 29 Ok(Self {
26 version: userlist_get_current_version(config.clone(), list_id, mod_id)?, 30 version: userlist_get_current_version(config, list_id, mod_id)?,
27 set: userlist_get_set_version(config.clone(), list_id, mod_id)? 31 set: userlist_get_set_version(config, list_id, mod_id)?,
28 }) 32 })
29 } 33 }
30} 34}
@@ -39,18 +43,18 @@ struct ExportList {
39} 43}
40 44
41impl ExportList { 45impl ExportList {
42 pub fn from(config: Cfg, list_id: String, download: bool) -> MLE<Self> { 46 pub fn from(config: &Cfg, list_id: &str, download: bool) -> MLE<Self> {
43 let list = lists_get(config.clone(), String::from(&list_id))?; 47 let list = lists_get(config, list_id)?;
44 48
45 let mut dl_folder = None; 49 let mut dl_folder = None;
46 if download { 50 if download {
47 dl_folder = Some(list.download_folder) 51 dl_folder = Some(list.download_folder)
48 }; 52 };
49 53
50 let mods = userlist_get_all_ids(config.clone(), &list_id)?; 54 let mods = userlist_get_all_ids(config, list_id)?;
51 let mut versions = vec![]; 55 let mut versions = vec![];
52 for m in mods { 56 for m in mods {
53 versions.push(ExportVersion::from(config.clone(), &list_id, &m)?) 57 versions.push(ExportVersion::from(config, list_id, &m)?)
54 } 58 }
55 59
56 Ok(Self { 60 Ok(Self {
@@ -63,29 +67,46 @@ impl ExportList {
63 } 67 }
64} 68}
65 69
66pub fn export(config: Cfg, list: Option<String>) -> MLE<()> { 70pub fn export(config: &Cfg, list: Option<String>) -> MLE<()> {
71 let progress = ProgressBar::new_spinner();
72 progress.set_style(ProgressStyle::with_template(STYLE_OPERATION).unwrap());
73
67 let mut list_ids: Vec<String> = vec![]; 74 let mut list_ids: Vec<String> = vec![];
68 if list.is_none() { 75 if list.is_none() {
69 list_ids = lists_get_all_ids(config.clone())?; 76 list_ids = lists_get_all_ids(config)?;
70 } else { 77 } else {
71 list_ids.push(lists_get(config.clone(), list.unwrap())?.id); 78 list_ids.push(lists_get(config, &list.unwrap())?.id);
72 } 79 }
80
73 let mut lists: Vec<ExportList> = vec![]; 81 let mut lists: Vec<ExportList> = vec![];
74 for list_id in list_ids { 82 for list_id in list_ids {
75 lists.push(ExportList::from(config.clone(), list_id, true)?); 83 progress.set_message(format!("Export {}", list_id));
84 //TODO download option/ new download on import
85 lists.push(ExportList::from(config, &list_id, true)?);
76 } 86 }
77 87
78 let toml = toml::to_string(&Export { lists })?; 88 let toml = toml::to_string(&Export { lists })?;
79 89
80 let filestr = dirs::home_dir().unwrap().join("mlexport.toml"); 90 let filestr = dirs::home_dir()
91 .unwrap()
92 .join("mlexport.toml")
93 .into_os_string()
94 .into_string()
95 .unwrap();
81 96
82 let mut file = File::create(filestr.into_os_string().into_string().unwrap().as_str())?; 97 progress.set_message("Create file");
98 let mut file = File::create(&filestr)?;
83 file.write_all(toml.as_bytes())?; 99 file.write_all(toml.as_bytes())?;
100 progress.finish_with_message(format!("Exported to {}", filestr));
84 101
85 Ok(()) 102 Ok(())
86} 103}
87 104
88pub async fn import(config: Cfg, file_str: String, direct_download: bool) -> MLE<()> { 105pub async fn import(
106 config: &Cfg,
107 file_str: &str,
108 direct_download: bool,
109) -> MLE<()> {
89 let mut file = File::open(file_str)?; 110 let mut file = File::open(file_str)?;
90 let mut content = String::new(); 111 let mut content = String::new();
91 file.read_to_string(&mut content)?; 112 file.read_to_string(&mut content)?;
@@ -99,18 +120,21 @@ pub async fn import(config: Cfg, file_str: String, direct_download: bool) -> MLE
99 download_folder: exportlist.download_folder.ok_or("NO_DL").unwrap(), 120 download_folder: exportlist.download_folder.ok_or("NO_DL").unwrap(),
100 }; 121 };
101 lists_insert( 122 lists_insert(
102 config.clone(), 123 config,
103 list.id.clone(), 124 &list.id,
104 list.mc_version.clone(), 125 &list.mc_version,
105 list.modloader.clone(), 126 &list.modloader,
106 String::from(&list.download_folder), 127 &list.download_folder,
107 )?; 128 )?;
108 129
109 let mut ver_ids = vec![]; 130 let mut ver_ids = vec![];
110 for id in exportlist.versions { 131 for id in exportlist.versions {
111 ver_ids.push(AddMod { id: IDSelector::VersionID(id.version), set_version: id.set} ); 132 ver_ids.push(AddMod {
133 id: IDSelector::VersionID(id.version),
134 set_version: id.set,
135 });
112 } 136 }
113 mod_add(config.clone(), ver_ids, list, direct_download).await?; 137 mod_add(config, ver_ids, list, direct_download).await?;
114 } 138 }
115 Ok(()) 139 Ok(())
116} 140}
diff --git a/src/commands/list.rs b/src/commands/list.rs
index 4aa4306..3665446 100644
--- a/src/commands/list.rs
+++ b/src/commands/list.rs
@@ -1,11 +1,13 @@
1use indicatif::{ProgressBar, ProgressStyle};
2
1use crate::{ 3use crate::{
2 config::Cfg, 4 config::Cfg,
3 db::{ 5 db::{
4 config_change_current_list, config_get_current_list, lists_get, lists_insert, lists_remove, 6 config_change_current_list, config_get_current_list, lists_get,
5 lists_version, lists_get_all_ids, 7 lists_get_all_ids, lists_insert, lists_remove, lists_version,
6 }, 8 },
7 error::{MLE, MLError, ErrorType}, 9 error::{ErrorType, MLError, MLE},
8 update, Modloader, 10 update, Modloader, STYLE_OPERATION,
9}; 11};
10 12
11#[derive(Debug, Clone, PartialEq, Eq)] 13#[derive(Debug, Clone, PartialEq, Eq)]
@@ -16,31 +18,47 @@ pub struct List {
16 pub download_folder: String, 18 pub download_folder: String,
17} 19}
18 20
19pub fn get_current_list(config: Cfg) -> MLE<List> { 21pub fn get_current_list(config: &Cfg) -> MLE<List> {
20 let id = config_get_current_list(config.clone())?; 22 let id = config_get_current_list(config)?;
21 lists_get(config, id) 23 lists_get(config, &id)
22} 24}
23 25
24pub fn list_add( 26pub fn list_add(
25 config: Cfg, 27 config: &Cfg,
26 id: String, 28 id: &str,
27 mc_version: String, 29 mc_version: &str,
28 modloader: Modloader, 30 modloader: &Modloader,
29 directory: String, 31 directory: &str,
30) -> MLE<()> { 32) -> MLE<()> {
31 lists_insert(config, id, mc_version, modloader, directory) 33 let p = ProgressBar::new_spinner();
34 p.set_style(ProgressStyle::with_template(STYLE_OPERATION).unwrap());
35 p.set_message(format!("Create {}", id));
36 lists_insert(config, id, mc_version, modloader, directory)?;
37 p.finish_with_message(format!("Created {}", id));
38 Ok(())
32} 39}
33 40
34pub fn list_change(config: Cfg, id: String) -> MLE<()> { 41pub fn list_change(config: &Cfg, id: &str) -> MLE<()> {
35 if lists_get_all_ids(config.clone())?.into_iter().find(|l| l == &id).is_none() { 42 let p = ProgressBar::new_spinner();
43 p.set_style(ProgressStyle::with_template(STYLE_OPERATION).unwrap());
44 p.set_message(format!("Change default list to {}", id));
45
46 if !lists_get_all_ids(config)?.into_iter().any(|l| l == id) {
36 return Err(MLError::new(ErrorType::ArgumentError, "List not found")); 47 return Err(MLError::new(ErrorType::ArgumentError, "List not found"));
37 }; 48 };
38 println!("Change default list to: {}", id); 49 config_change_current_list(config, id)?;
39 config_change_current_list(config, id) 50
51 p.finish_with_message(format!("Changed default list to {}", id));
52 Ok(())
40} 53}
41 54
42pub fn list_remove(config: Cfg, id: String) -> MLE<()> { 55pub fn list_remove(config: &Cfg, id: &str) -> MLE<()> {
43 lists_remove(config, id) 56 let p = ProgressBar::new_spinner();
57 p.set_style(ProgressStyle::with_template(STYLE_OPERATION).unwrap());
58 p.set_message(format!("Remove {}", id));
59 lists_remove(config, id)?;
60 p.finish_with_message(format!("Removed {}", id));
61 Ok(())
44} 62}
45 63
46///Changing the current lists version and updating it 64///Changing the current lists version and updating it
@@ -50,31 +68,34 @@ pub fn list_remove(config: Cfg, id: String) -> MLE<()> {
50/// * `config` - The current config 68/// * `config` - The current config
51/// * `args` - All args, to extract the new version 69/// * `args` - All args, to extract the new version
52pub async fn list_version( 70pub async fn list_version(
53 config: Cfg, 71 config: &Cfg,
54 id: String, 72 id: &str,
55 mc_version: String, 73 mc_version: String,
56 download: bool, 74 download: bool,
57 delete: bool, 75 delete: bool,
58) -> MLE<()> { 76) -> MLE<()> {
59 println!( 77 let p = ProgressBar::new_spinner();
78 p.set_style(ProgressStyle::with_template(STYLE_OPERATION).unwrap());
79 p.set_message(format!(
60 "Change version for list {} to minecraft version: {}", 80 "Change version for list {} to minecraft version: {}",
61 id, mc_version 81 id, mc_version
62 ); 82 ));
63 83
64 lists_version(config.clone(), &id, &mc_version)?; 84 lists_version(config, id, &mc_version)?;
85
86 p.finish_with_message(format!(
87 "Changed version for list {} to minecraft version: {}",
88 id, mc_version
89 ));
65 90
66 println!( 91 let list = lists_get(config, id)?;
67 "\nCheck for updates for new minecraft version in list {}",
68 id
69 );
70 let list = lists_get(config.clone(), id)?;
71 update(config, vec![list], true, download, delete).await 92 update(config, vec![list], true, download, delete).await
72} 93}
73 94
74pub fn list_list(config: Cfg) -> MLE<()> { 95pub fn list_list(config: &Cfg) -> MLE<()> {
75 let lists = lists_get_all_ids(config.clone())?; 96 let lists = lists_get_all_ids(config)?;
76 for list in lists { 97 for list in lists {
77 let l = lists_get(config.clone(), list)?; 98 let l = lists_get(config, &list)?;
78 println!("{}: | {} | {}", l.id, l.mc_version, l.modloader) 99 println!("{}: | {} | {}", l.id, l.mc_version, l.modloader)
79 } 100 }
80 Ok(()) 101 Ok(())
diff --git a/src/commands/modification.rs b/src/commands/modification.rs
index 9a1a651..4488b70 100644
--- a/src/commands/modification.rs
+++ b/src/commands/modification.rs
@@ -1,24 +1,30 @@
1use std::{io::Write, collections::HashMap}; 1use std::collections::HashMap;
2
3use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
2 4
3use crate::{ 5use crate::{
4 config::Cfg, 6 config::Cfg,
5 db::{ 7 db::{
6 lists_get_all_ids, mods_get_id, mods_insert, mods_remove, userlist_get_all_ids, 8 lists_get_all_ids, mods_get_id, mods_get_info, mods_insert,
7 userlist_get_current_version, userlist_insert, userlist_remove, mods_get_info, 9 mods_remove, userlist_get_all_ids, userlist_get_current_version,
10 userlist_insert, userlist_remove,
8 }, 11 },
9 error::{ErrorType, MLError, MLE}, 12 error::{ErrorType, MLError, MLE},
10 files::{delete_version, download_versions}, 13 files::{delete_version, download_versions},
11 modrinth::{extract_current_version, get_raw_versions, project, projects, versions, Version}, 14 modrinth::{
12 List, 15 extract_current_version, get_raw_versions, project, projects, versions,
16 Version,
17 },
18 List, PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION,
13}; 19};
14 20
15#[derive(Debug, Clone)] 21#[derive(Debug)]
16pub struct AddMod { 22pub struct AddMod {
17 pub id: IDSelector, 23 pub id: IDSelector,
18 pub set_version: bool 24 pub set_version: bool,
19} 25}
20 26
21#[derive(Debug, Clone, PartialEq, Eq)] 27#[derive(Debug, PartialEq, Eq)]
22pub enum IDSelector { 28pub enum IDSelector {
23 ModificationID(String), 29 ModificationID(String),
24 VersionID(String), 30 VersionID(String),
@@ -36,54 +42,76 @@ pub struct ProjectInfo {
36} 42}
37 43
38pub async fn mod_add( 44pub async fn mod_add(
39 config: Cfg, 45 config: &Cfg,
40 mods: Vec<AddMod>, 46 mods: Vec<AddMod>,
41 list: List, 47 list: List,
42 direct_download: bool, 48 direct_download: bool,
43) -> MLE<()> { 49) -> MLE<()> {
44 println!("Add mods to {}", list.id); 50 let mp = MultiProgress::new();
45 println!(" └Add mods:");
46 51
47 let mut mod_ids: Vec<(String, bool)> = Vec::new(); 52 let mut mod_ids: Vec<(String, bool)> = Vec::new();
48 let mut ver_ids: Vec<(String, bool)> = Vec::new(); 53 let mut ver_ids: Vec<(String, bool)> = Vec::new();
49 54
55 let add_p = mp.add(ProgressBar::new(mods.len().try_into().unwrap()));
56 add_p.set_style(
57 ProgressStyle::with_template(STYLE_BAR_POS)
58 .unwrap()
59 .progress_chars(PROGRESS_CHARS),
60 );
61 add_p.set_message("Sort ids");
62
50 //"Sort" project ids from version ids to be able to handle them differently but in a batch 63 //"Sort" project ids from version ids to be able to handle them differently but in a batch
51 for m in mods { 64 for m in mods {
65 add_p.inc(1);
52 match m.id { 66 match m.id {
53 IDSelector::ModificationID(pid) => mod_ids.push((pid, m.set_version)), 67 IDSelector::ModificationID(pid) => {
68 mod_ids.push((pid, m.set_version))
69 }
54 IDSelector::VersionID(vid) => ver_ids.push((vid, m.set_version)), 70 IDSelector::VersionID(vid) => ver_ids.push((vid, m.set_version)),
55 } 71 }
56 } 72 }
57 73
74 add_p.set_message("Get infos");
75
58 let mut projectinfo: Vec<ProjectInfo> = Vec::new(); 76 let mut projectinfo: Vec<ProjectInfo> = Vec::new();
59 if !mod_ids.is_empty() { 77 if !mod_ids.is_empty() {
60 projectinfo.append(&mut get_mod_infos(config.clone(), mod_ids, list.clone()).await?) 78 projectinfo
79 .append(&mut get_mod_infos(config, mod_ids, list.clone()).await?);
61 }; 80 };
62 if !ver_ids.is_empty() { 81 if !ver_ids.is_empty() {
63 projectinfo.append(&mut get_ver_info(config.clone(), ver_ids).await?) 82 projectinfo.append(&mut get_ver_info(config, ver_ids).await?);
64 }; 83 };
65 84
66 if projectinfo.is_empty() { 85 if projectinfo.is_empty() {
67 return Err(MLError::new(ErrorType::ArgumentError, "NO_IDS?")); 86 return Err(MLError::new(ErrorType::ArgumentError, "NO_IDS?"));
68 }; 87 };
69 88
89 add_p.set_message("Add mods to database");
90
70 let mut downloadstack: Vec<Version> = Vec::new(); 91 let mut downloadstack: Vec<Version> = Vec::new();
71 92
72 //Adding each mod to the lists and downloadstack 93 //Adding each mod to the lists and downloadstack
73 if projectinfo.len() == 1 { 94 let project_p = mp.insert_before(
74 println!(" └Insert mod in list {} and save infos", list.id); 95 &add_p,
75 } else { 96 ProgressBar::new(projectinfo.len().try_into().unwrap()),
76 println!(" └Insert mods in list {} and save infos", list.id); 97 );
77 } 98 project_p.set_style(
99 ProgressStyle::with_template(STYLE_BAR_POS)
100 .unwrap()
101 .progress_chars(PROGRESS_CHARS),
102 );
78 103
79 for project in projectinfo { 104 for project in projectinfo {
105 project_p.set_message(format!("Add {}", project.title));
106
80 let current_version_id = if project.current_version.is_none() { 107 let current_version_id = if project.current_version.is_none() {
81 String::from("NONE") 108 String::from("NONE")
82 } else { 109 } else {
83 project.current_version.clone().unwrap().id 110 project.current_version.clone().unwrap().id
84 }; 111 };
112
85 match userlist_insert( 113 match userlist_insert(
86 config.clone(), 114 config,
87 &list.id, 115 &list.id,
88 &project.mod_id, 116 &project.mod_id,
89 &current_version_id, 117 &current_version_id,
@@ -92,7 +120,10 @@ pub async fn mod_add(
92 project.set_version, 120 project.set_version,
93 ) { 121 ) {
94 Err(e) => { 122 Err(e) => {
95 let expected_err = format!("SQL: UNIQUE constraint failed: {}.mod_id", list.id); 123 let expected_err = format!(
124 "SQL: UNIQUE constraint failed: {}.mod_id",
125 list.id
126 );
96 if e.to_string() == expected_err { 127 if e.to_string() == expected_err {
97 Err(MLError::new( 128 Err(MLError::new(
98 ErrorType::ModError, 129 ErrorType::ModError,
@@ -106,7 +137,7 @@ pub async fn mod_add(
106 }?; 137 }?;
107 138
108 match mods_insert( 139 match mods_insert(
109 config.clone(), 140 config,
110 &project.mod_id, 141 &project.mod_id,
111 &project.slug, 142 &project.slug,
112 &project.title, 143 &project.title,
@@ -124,18 +155,35 @@ pub async fn mod_add(
124 if project.current_version.is_some() { 155 if project.current_version.is_some() {
125 downloadstack.push(project.current_version.unwrap()) 156 downloadstack.push(project.current_version.unwrap())
126 }; 157 };
158
159 project_p.inc(1);
127 } 160 }
128 161
162 project_p.finish_with_message("Added all mods to the database");
163
129 //Download all the added mods 164 //Download all the added mods
130 if direct_download { 165 if direct_download {
131 download_versions(list.clone(), config.clone(), downloadstack).await?; 166 add_p.set_message("Download mods");
167 download_versions(
168 list.clone(),
169 config.clone(),
170 downloadstack,
171 &mp,
172 &add_p,
173 )
174 .await?;
132 }; 175 };
133 176
177 add_p.finish_with_message("Added all mods");
178
134 Ok(()) 179 Ok(())
135} 180}
136 181
137async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) -> MLE<Vec<ProjectInfo>> { 182async fn get_mod_infos(
138 183 config: &Cfg,
184 mod_ids: Vec<(String, bool)>,
185 list: List,
186) -> MLE<Vec<ProjectInfo>> {
139 let mut setmap: HashMap<String, bool> = HashMap::new(); 187 let mut setmap: HashMap<String, bool> = HashMap::new();
140 188
141 let mut ids = vec![]; 189 let mut ids = vec![];
@@ -154,8 +202,6 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) ->
154 _ => panic!("PANIC"), 202 _ => panic!("PANIC"),
155 }; 203 };
156 for project in m_projects { 204 for project in m_projects {
157 println!("\t└{}", project.title);
158 println!("\t └Get versions");
159 let available_versions = versions( 205 let available_versions = versions(
160 &config.apis.modrinth, 206 &config.apis.modrinth,
161 String::from(&project.id), 207 String::from(&project.id),
@@ -167,8 +213,8 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) ->
167 let current_version: Option<Version>; 213 let current_version: Option<Version>;
168 let file: String; 214 let file: String;
169 if !available_versions.is_empty() { 215 if !available_versions.is_empty() {
170 let current_id = extract_current_version(available_versions.clone())?; 216 let current_id =
171 println!("\t └Current version: {}", current_id); 217 extract_current_version(available_versions.clone())?;
172 218
173 current_version = Some( 219 current_version = Some(
174 available_versions 220 available_versions
@@ -177,19 +223,15 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) ->
177 .find(|v| v.id == current_id) 223 .find(|v| v.id == current_id)
178 .unwrap(), 224 .unwrap(),
179 ); 225 );
180 226
181 // match primary, if none? 227 // match primary, if none?
182 let files = current_version 228 let files = current_version.clone().ok_or("").unwrap().files;
183 .clone()
184 .ok_or("")
185 .unwrap()
186 .files;
187 229
188 file = match files.clone().into_iter().find(|f| f.primary) { 230 file = match files.clone().into_iter().find(|f| f.primary) {
189 Some(f) => f, 231 Some(f) => f,
190 None => { files[0].clone() } 232 None => files[0].clone(),
191 } 233 }
192 .url; 234 .url;
193 235
194 for ver in available_versions { 236 for ver in available_versions {
195 available_versions_vec.push(ver.id); 237 available_versions_vec.push(ver.id);
@@ -197,15 +239,14 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) ->
197 239
198 projectinfo.push(ProjectInfo { 240 projectinfo.push(ProjectInfo {
199 mod_id: String::from(&project.id), 241 mod_id: String::from(&project.id),
200 slug: project.slug, 242 slug: project.slug.clone(),
201 title: project.title, 243 title: project.title,
202 current_version, 244 current_version,
203 applicable_versions: available_versions_vec, 245 applicable_versions: available_versions_vec,
204 download_link: file, 246 download_link: file,
205 set_version: setmap.get(&project.id).unwrap().clone(), 247 set_version: *setmap.get(&project.slug).unwrap(),
206 }) 248 })
207 } else { 249 } else {
208 println!("\t └There's currently no mod version for your specified target");
209 current_version = None; 250 current_version = None;
210 file = String::from("NONE"); 251 file = String::from("NONE");
211 available_versions_vec.push(String::from("NONE")); 252 available_versions_vec.push(String::from("NONE"));
@@ -216,7 +257,7 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) ->
216 current_version, 257 current_version,
217 applicable_versions: available_versions_vec, 258 applicable_versions: available_versions_vec,
218 download_link: file, 259 download_link: file,
219 set_version: setmap.get(&project.id).unwrap().clone(), 260 set_version: *setmap.get(&project.id).unwrap(),
220 }) 261 })
221 } 262 }
222 } 263 }
@@ -224,8 +265,10 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) ->
224 Ok(projectinfo) 265 Ok(projectinfo)
225} 266}
226 267
227async fn get_ver_info(config: Cfg, ver_ids: Vec<(String, bool)>) -> MLE<Vec<ProjectInfo>> { 268async fn get_ver_info(
228 269 config: &Cfg,
270 ver_ids: Vec<(String, bool)>,
271) -> MLE<Vec<ProjectInfo>> {
229 let mut setmap: HashMap<String, bool> = HashMap::new(); 272 let mut setmap: HashMap<String, bool> = HashMap::new();
230 273
231 let mut ids = vec![]; 274 let mut ids = vec![];
@@ -248,14 +291,15 @@ async fn get_ver_info(config: Cfg, ver_ids: Vec<(String, bool)>) -> MLE<Vec<Proj
248 291
249 for (i, project) in v_projects.into_iter().enumerate() { 292 for (i, project) in v_projects.into_iter().enumerate() {
250 let version = &v_versions[i]; 293 let version = &v_versions[i];
251 println!("\t└{}({})", project.title, version.id); 294
252 let file = version 295 let files = version.clone().files;
253 .clone() 296
254 .files 297 let file = match files.clone().into_iter().find(|f| f.primary) {
255 .into_iter() 298 Some(f) => f,
256 .find(|f| f.primary) 299 None => files[0].clone(),
257 .unwrap() 300 }
258 .url; 301 .url;
302
259 projectinfo.push(ProjectInfo { 303 projectinfo.push(ProjectInfo {
260 mod_id: String::from(&project.id), 304 mod_id: String::from(&project.id),
261 slug: project.slug, 305 slug: project.slug,
@@ -263,7 +307,7 @@ async fn get_ver_info(config: Cfg, ver_ids: Vec<(String, bool)>) -> MLE<Vec<Proj
263 current_version: Some(version.clone()), 307 current_version: Some(version.clone()),
264 applicable_versions: vec![String::from(&version.id)], 308 applicable_versions: vec![String::from(&version.id)],
265 download_link: file, 309 download_link: file,
266 set_version: setmap.get(&version.id).unwrap().clone(), 310 set_version: *setmap.get(&version.id).unwrap(),
267 }) 311 })
268 } 312 }
269 Ok(projectinfo) 313 Ok(projectinfo)
@@ -275,48 +319,45 @@ async fn get_ver_info(config: Cfg, ver_ids: Vec<(String, bool)>) -> MLE<Vec<Proj
275/// * `config` - config struct 319/// * `config` - config struct
276/// * `id` - name, slug or id of the mod 320/// * `id` - name, slug or id of the mod
277/// * `list` - List struct 321/// * `list` - List struct
278pub fn mod_remove(config: Cfg, id: &str, list: List) -> MLE<()> { 322pub fn mod_remove(config: &Cfg, id: &str, list: &List) -> MLE<()> {
323 let progress = ProgressBar::new_spinner();
324 progress.set_style(ProgressStyle::with_template(STYLE_OPERATION).unwrap());
325
279 let mod_id = mods_get_id(&config.data, id)?; 326 let mod_id = mods_get_id(&config.data, id)?;
280 327
281 println!("Remove mod {} from {}", mods_get_info(config.clone(), &mod_id)?.title, list.id); 328 let info = mods_get_info(config, &mod_id)?;
282 let version = userlist_get_current_version(config.clone(), &list.id, &mod_id)?;
283 329
284 print!(" └Remove from list"); 330 progress.set_message(format!("Remove {} from {}", info.title, list.id));
285 //Force flush of stdout, else print! doesn't print instantly
286 std::io::stdout().flush()?;
287 userlist_remove(config.clone(), &list.id, &mod_id)?;
288 println!(" ✓");
289 331
290 print!(" └Delete file"); 332 let version = userlist_get_current_version(config, &list.id, &mod_id)?;
291 //Force flush of stdout, else print! doesn't print instantly 333
292 std::io::stdout().flush()?; 334 userlist_remove(config, &list.id, &mod_id)?;
335
336 progress.set_message("Delete file");
293 match delete_version(list, version) { 337 match delete_version(list, version) {
294 Ok(_) => (), 338 Ok(_) => (),
295 Err(err) => { 339 Err(err) => {
296 if err.to_string() != "User input not accepted: VERSION_NOT_FOUND_IN_FILES" { 340 if err.to_string()
341 != "User input not accepted: VERSION_NOT_FOUND_IN_FILES"
342 {
297 return Err(err); 343 return Err(err);
298 }; 344 };
299 () 345 }
300 },
301 }; 346 };
302 println!(" ✓");
303 347
304 print!(" └Clean main db table"); 348 progress.set_message("Check main list");
305 //Force flush of stdout, else print! doesn't print instantly 349 let list_ids = lists_get_all_ids(config)?;
306 std::io::stdout().flush()?;
307 let list_ids = lists_get_all_ids(config.clone())?;
308 350
309 // Remove mod from main list if not used elsewhere 351 // Remove mod from main list if not used elsewhere
310 let mut mod_used = false; 352 let mut mod_used = false;
311 for id in list_ids { 353 for id in list_ids {
312 let mods = match userlist_get_all_ids(config.clone(), &id) { 354 let mods = match userlist_get_all_ids(config, &id) {
313 Ok(m) => m, 355 Ok(m) => m,
314 Err(err) => { 356 Err(err) => {
315 if err.to_string() == "Database: NO_MODS_USERLIST" { 357 if err.to_string() == "Database: NO_MODS_USERLIST" {
316 println!(" ✓");
317 return Ok(()); 358 return Ok(());
318 }; 359 };
319 return Err(err) 360 return Err(err);
320 } 361 }
321 }; 362 };
322 if mods.contains(&mod_id) { 363 if mods.contains(&mod_id) {
@@ -326,9 +367,14 @@ pub fn mod_remove(config: Cfg, id: &str, list: List) -> MLE<()> {
326 } 367 }
327 368
328 if !mod_used { 369 if !mod_used {
329 mods_remove(config, mod_id)?; 370 progress.set_message("Remove from main list");
371 mods_remove(config, &mod_id)?;
330 }; 372 };
331 println!(" ✓"); 373
374 progress.finish_with_message(format!(
375 "Removed {} from {}",
376 info.title, list.id
377 ));
332 378
333 Ok(()) 379 Ok(())
334} 380}
diff --git a/src/commands/update.rs b/src/commands/update.rs
index d3a282b..c19c02c 100644
--- a/src/commands/update.rs
+++ b/src/commands/update.rs
@@ -1,49 +1,81 @@
1use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
2
1use crate::{ 3use crate::{
2 config::Cfg, 4 config::Cfg,
3 db::{ 5 db::{
4 mods_get_info, userlist_change_versions, userlist_get_all_ids, 6 mods_get_info, userlist_change_versions, userlist_get_all_ids,
5 userlist_get_applicable_versions, userlist_get_current_version, userlist_get_set_version, 7 userlist_get_applicable_versions, userlist_get_current_version,
8 userlist_get_set_version,
6 }, 9 },
7 error::{ErrorType, MLError, MLE}, 10 error::{ErrorType, MLError, MLE},
8 files::{clean_list_dir, delete_version, disable_version, download_versions}, 11 files::{
12 clean_list_dir, delete_version, disable_version, download_versions,
13 },
9 modrinth::{extract_current_version, versions, Version}, 14 modrinth::{extract_current_version, versions, Version},
10 List, 15 List, PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION,
11}; 16};
12 17
13pub async fn update( 18pub async fn update(
14 config: Cfg, 19 config: &Cfg,
15 liststack: Vec<List>, 20 liststack: Vec<List>,
16 clean: bool, 21 clean: bool,
17 direct_download: bool, 22 direct_download: bool,
18 delete_old: bool, 23 delete_old: bool,
19) -> MLE<()> { 24) -> MLE<()> {
25 let mp = MultiProgress::new();
26
27 let update_p =
28 mp.add(ProgressBar::new(liststack.len().try_into().unwrap()));
29 update_p.set_style(
30 ProgressStyle::with_template(STYLE_BAR_POS)
31 .unwrap()
32 .progress_chars(PROGRESS_CHARS),
33 );
34
20 for current_list in liststack { 35 for current_list in liststack {
21 println!("Update mods in {}", current_list.id); 36 update_p.set_message(format!("Update {}", current_list.id));
22 let mods = userlist_get_all_ids(config.clone(), &current_list.id)?;
23 37
24 let mut current_versions: Vec<(String, String)> = vec![]; 38 let list_p = mp.insert_before(&update_p, ProgressBar::new(2));
39 list_p
40 .set_style(ProgressStyle::with_template(STYLE_OPERATION).unwrap());
41 list_p.set_message("Update mods");
42
43 let mods = userlist_get_all_ids(config, &current_list.id)?;
44
45 let list_u_p = mp.insert_before(
46 &list_p,
47 ProgressBar::new(mods.len().try_into().unwrap()),
48 );
49 list_u_p.set_style(
50 ProgressStyle::with_template(STYLE_BAR_POS)
51 .unwrap()
52 .progress_chars(PROGRESS_CHARS),
53 );
25 54
55 let mut current_versions: Vec<(String, String)> = vec![];
26 let mut updatestack: Vec<Version> = vec![]; 56 let mut updatestack: Vec<Version> = vec![];
27 57
28 for id in mods { 58 for id in mods {
29 let info = mods_get_info(config.clone(), &id)?; 59 let info = mods_get_info(config, &id)?;
30 println!(" {}", info.title); 60 list_u_p.set_message(format!("Update {}", info.title));
31 61
32 if userlist_get_set_version(config.clone(), &current_list.id, &id)? { 62 //Skip check if version is set
33 println!(" │ └Set version, skipping update"); 63 if userlist_get_set_version(config, &current_list.id, &id)? {
64 list_u_p.inc(1);
34 continue; 65 continue;
35 } 66 }
36 67
37 //Getting current installed version for disable or delete 68 //Getting current installed version for disable or delete
38 let disable_version = 69 let disable_version =
39 userlist_get_current_version(config.clone(), &current_list.id, &id)?; 70 userlist_get_current_version(config, &current_list.id, &id)?;
40 71
41 updatestack.push( 72 updatestack.push(
42 match specific_update( 73 match specific_update(
43 config.clone(), 74 config,
44 clean, 75 clean,
45 current_list.clone(), 76 current_list.clone(),
46 String::from(&id), 77 &id,
78 &list_u_p,
47 ) 79 )
48 .await 80 .await
49 { 81 {
@@ -53,46 +85,94 @@ pub async fn update(
53 } 85 }
54 Err(e) => { 86 Err(e) => {
55 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" { 87 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" {
56 println!(
57 " │ └No new version found for the specified minecraft version"
58 );
59 } else { 88 } else {
60 return Err(e); 89 return Err(e);
61 }; 90 };
91 list_u_p.inc(1);
62 continue; 92 continue;
63 } 93 }
64 }, 94 },
65 ) 95 );
96 list_u_p.inc(1);
66 } 97 }
67 98
99 list_u_p.finish_with_message(format!(
100 "Updated mods in {}",
101 current_list.id
102 ));
103
68 if clean { 104 if clean {
105 list_p.set_message("Cleaning");
69 clean_list_dir(&current_list)?; 106 clean_list_dir(&current_list)?;
70 }; 107 };
71 108
72 if direct_download && !updatestack.is_empty() { 109 if direct_download && !updatestack.is_empty() {
73 download_versions(current_list.clone(), config.clone(), updatestack).await?; 110 download_versions(
111 current_list.clone(),
112 config.clone(),
113 updatestack,
114 &mp,
115 &list_p,
116 )
117 .await?;
74 118
75 //Disable old versions 119 //Disable old versions
76 if !clean { 120 if !clean {
121 let d_p = mp.insert_before(
122 &list_p,
123 ProgressBar::new(
124 current_versions.len().try_into().unwrap(),
125 ),
126 );
127 d_p.set_style(
128 ProgressStyle::with_template(STYLE_BAR_POS)
129 .unwrap()
130 .progress_chars(PROGRESS_CHARS),
131 );
77 for ver in current_versions { 132 for ver in current_versions {
78 if delete_old { 133 if delete_old {
79 println!(" └Delete version {}", ver.0); 134 d_p.set_message(format!("Delete version {}", ver.0));
80 delete_version(current_list.clone(), ver.0)?; 135 d_p.inc(1);
136 delete_version(&current_list, ver.0)?;
81 } else if ver.0 != "NONE" { 137 } else if ver.0 != "NONE" {
82 println!(" └Disable version {}", ver.0); 138 d_p.set_message(format!("Disable version {}", ver.0));
83 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?; 139 d_p.inc(1);
140 disable_version(
141 config,
142 current_list.clone(),
143 ver.0,
144 ver.1,
145 )?;
84 }; 146 };
85 } 147 }
148
149 let del_msg = if delete_old {
150 "Deleted all old versions"
151 } else {
152 "Disabled all old versions"
153 };
154
155 d_p.finish_with_message(del_msg);
86 } 156 }
87 }; 157 };
158 list_p.finish_with_message(format!("Updated {}", current_list.id));
159 update_p.inc(1);
88 } 160 }
89 161
162 update_p.finish_with_message("Updated all lists");
163
90 Ok(()) 164 Ok(())
91} 165}
92 166
93async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> MLE<Version> { 167async fn specific_update(
168 config: &Cfg,
169 clean: bool,
170 list: List,
171 id: &str,
172 progress: &ProgressBar,
173) -> MLE<Version> {
94 let applicable_versions = 174 let applicable_versions =
95 versions(&config.apis.modrinth, String::from(&id), list.clone()).await; 175 versions(&config.apis.modrinth, String::from(id), list.clone()).await;
96 176
97 let mut versions: Vec<String> = vec![]; 177 let mut versions: Vec<String> = vec![];
98 178
@@ -108,19 +188,19 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML
108 if clean 188 if clean
109 || (versions.join("|") 189 || (versions.join("|")
110 != userlist_get_applicable_versions( 190 != userlist_get_applicable_versions(
111 config.clone(), 191 config,
112 String::from(&list.id), 192 String::from(&list.id),
113 String::from(&id), 193 String::from(id),
114 )?) 194 )?)
115 { 195 {
116 let current_str = extract_current_version(applicable_versions.clone())?; 196 let current_str = extract_current_version(applicable_versions.clone())?;
117 197
118 if clean { 198 if !clean {
119 println!("\t └Add version to downloadstack"); 199 progress.println(format!(
120 } else { 200 "Found new version for {}",
121 println!("\t └Get versions for specified minecraft versions"); 201 mods_get_info(config, id).unwrap().title
122 println!("\t └New current version: {}", current_str); 202 ));
123 }; 203 }
124 204
125 //get new versions 205 //get new versions
126 let current_ver = match applicable_versions 206 let current_ver = match applicable_versions
@@ -133,73 +213,27 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML
133 }?; 213 }?;
134 current.push(current_ver.clone()); 214 current.push(current_ver.clone());
135 215
136 let link = match current_ver 216 let files = &current_ver.files;
137 .files 217
138 .into_iter() 218 let link = match files.clone().into_iter().find(|f| f.primary) {
139 .find(|f| f.primary) 219 Some(f) => f,
140 .ok_or("!no primary in links") 220 None => files[0].clone(),
141 { 221 }
142 Ok(p) => Ok(p),
143 Err(e) => Err(MLError::new(ErrorType::Other, e)),
144 }?
145 .url; 222 .url;
146 userlist_change_versions(config, list.id, current_str, versions.join("|"), link, id)?; 223
224 userlist_change_versions(
225 config,
226 list.id,
227 current_str,
228 versions.join("|"),
229 link,
230 id.to_string(),
231 )?;
147 } 232 }
148 233
149 if current.is_empty() { 234 if current.is_empty() {
150 return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE")); 235 return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE"));
151 }; 236 };
152 237
153 //println!(" └✔️");
154 Ok(current[0].clone()) 238 Ok(current[0].clone())
155} 239}
156
157// #[tokio::test]
158// async fn download_updates_test() {
159// use crate::{
160// modrinth::{Hash, Version, VersionFile, VersionType},
161// List, Modloader,
162// };
163//
164// let config = Cfg::init().unwrap();
165// let current_list = List {
166// id: String::from("..."),
167// mc_version: String::from("..."),
168// modloader: Modloader::Fabric,
169// download_folder: String::from("./dev/tests/dl"),
170// };
171//
172// let versions = vec![Version {
173// id: "dEqtGnT9".to_string(),
174// project_id: "kYuIpRLv".to_string(),
175// author_id: "Qnt13hO8".to_string(),
176// featured: true,
177// name: "1.2.2-1.19 - Fabric".to_string(),
178// version_number: "1.2.2-1.19".to_string(),
179// changelog: None,
180// date_published: "2022-11-02T17:41:43.072267Z".to_string(),
181// downloads: 58,
182// version_type: VersionType::release,
183// files: vec![VersionFile {
184// hashes: Hash {
185// sha1: "fdc6dc39427fc92cc1d7ad8b275b5b83325e712b".to_string(),
186// sha512: "5b372f00d6e5d6a5ef225c3897826b9f6a2be5506905f7f71b9e939779765b41be6f2a9b029cfc752ad0751d0d2d5f8bb4544408df1363eebdde15641e99a849".to_string()
187// },
188// url: "https://cdn.modrinth.com/data/kYuIpRLv/versions/dEqtGnT9/waveycapes-fabric-1.2.2-mc1.19.2.jar".to_string(),
189// filename: "waveycapes-fabric-1.2.2-mc1.19.2.jar".to_string(),
190// primary: true,
191// size: 323176
192// }],
193// game_versions: vec![
194// "1.19".to_string(),
195// "1.19.1".to_string(),
196// "1.19.2".to_string()
197// ],
198// loaders: vec![
199// "fabric".to_string()
200// ]
201// }];
202// assert!(download_versions(current_list, config, versions)
203// .await
204// .is_ok())
205// }