summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/apis/modrinth.rs7
-rw-r--r--src/commands/download.rs39
-rw-r--r--src/commands/io.rs34
-rw-r--r--src/commands/list.rs54
-rw-r--r--src/commands/modification.rs114
-rw-r--r--src/commands/update.rs95
-rw-r--r--src/config.rs6
-rw-r--r--src/db.rs41
-rw-r--r--src/error.rs6
-rw-r--r--src/files.rs77
-rw-r--r--src/lib.rs46
-rw-r--r--src/main.rs164
12 files changed, 406 insertions, 277 deletions
diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs
index 14ff266..fb3952d 100644
--- a/src/apis/modrinth.rs
+++ b/src/apis/modrinth.rs
@@ -127,7 +127,7 @@ pub enum GameVersionType {
127 release, 127 release,
128 snapshot, 128 snapshot,
129 alpha, 129 alpha,
130 beta 130 beta,
131} 131}
132 132
133async fn get(api: &str, path: &str) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> { 133async fn get(api: &str, path: &str) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> {
@@ -208,7 +208,10 @@ pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
208} 208}
209 209
210pub async fn get_game_versions() -> Vec<GameVersion> { 210pub async fn get_game_versions() -> Vec<GameVersion> {
211 let data = get("https://api.modrinth.com/v2/", "tag/game_version").await.unwrap().unwrap(); 211 let data = get("https://api.modrinth.com/v2/", "tag/game_version")
212 .await
213 .unwrap()
214 .unwrap();
212 215
213 serde_json::from_slice(&data).unwrap() 216 serde_json::from_slice(&data).unwrap()
214} 217}
diff --git a/src/commands/download.rs b/src/commands/download.rs
index 7aa0156..dd00ffb 100644
--- a/src/commands/download.rs
+++ b/src/commands/download.rs
@@ -1,7 +1,5 @@
1use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
1 2
2use indicatif::{MultiProgress, ProgressStyle, ProgressBar};
3
4use crate::{STYLE_BAR_POS, PROGRESS_CHARS};
5use crate::{config::Cfg, List}; 3use crate::{config::Cfg, List};
6use crate::{ 4use crate::{
7 db::userlist_get_all_current_versions_with_mods, 5 db::userlist_get_all_current_versions_with_mods,
@@ -11,12 +9,21 @@ use crate::{
11 }, 9 },
12 modrinth::get_raw_versions, 10 modrinth::get_raw_versions,
13}; 11};
14 12use crate::{PROGRESS_CHARS, STYLE_BAR_POS};
15pub async fn download(config: &Cfg, liststack: Vec<List>, clean: bool, delete_old: bool) -> MLE<()> { 13
16 14pub async fn download(
15 config: &Cfg,
16 liststack: Vec<List>,
17 clean: bool,
18 delete_old: bool,
19) -> MLE<()> {
17 let mp = MultiProgress::new(); 20 let mp = MultiProgress::new();
18 let download_p = mp.add(ProgressBar::new(liststack.len().try_into().unwrap())); 21 let download_p = mp.add(ProgressBar::new(liststack.len().try_into().unwrap()));
19 download_p.set_style(ProgressStyle::with_template(STYLE_BAR_POS).unwrap().progress_chars(PROGRESS_CHARS)); 22 download_p.set_style(
23 ProgressStyle::with_template(STYLE_BAR_POS)
24 .unwrap()
25 .progress_chars(PROGRESS_CHARS),
26 );
20 27
21 for current_list in liststack { 28 for current_list in liststack {
22 download_p.set_message(format!("Download in {}", current_list.id)); 29 download_p.set_message(format!("Download in {}", current_list.id));
@@ -67,17 +74,27 @@ pub async fn download(config: &Cfg, liststack: Vec<List>, clean: bool, delete_ol
67 ) 74 )
68 .await?; 75 .await?;
69 } else { 76 } else {
70 download_p.println(format!("There are no new versions to download for {}", current_list.id)); 77 download_p.println(format!(
78 "There are no new versions to download for {}",
79 current_list.id
80 ));
71 } 81 }
72 82
73 if !to_disable.is_empty() { 83 if !to_disable.is_empty() {
74 let d_p = mp.insert_before(&download_p, ProgressBar::new(to_disable.len().try_into().unwrap())); 84 let d_p = mp.insert_before(
75 d_p.set_style(ProgressStyle::with_template(STYLE_BAR_POS).unwrap().progress_chars(PROGRESS_CHARS)); 85 &download_p,
86 ProgressBar::new(to_disable.len().try_into().unwrap()),
87 );
88 d_p.set_style(
89 ProgressStyle::with_template(STYLE_BAR_POS)
90 .unwrap()
91 .progress_chars(PROGRESS_CHARS),
92 );
76 for ver in to_disable { 93 for ver in to_disable {
77 if delete_old { 94 if delete_old {
78 d_p.set_message(format!("Delete version {}", ver.1)); 95 d_p.set_message(format!("Delete version {}", ver.1));
79 d_p.inc(1); 96 d_p.inc(1);
80 delete_version(current_list.clone(), ver.1)?; 97 delete_version(&current_list, ver.1)?;
81 } else { 98 } else {
82 d_p.set_message(format!("Disable version {}", ver.1)); 99 d_p.set_message(format!("Disable version {}", ver.1));
83 d_p.inc(1); 100 d_p.inc(1);
diff --git a/src/commands/io.rs b/src/commands/io.rs
index 45e363e..2501583 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, list_id, mod_id)?, 30 version: userlist_get_current_version(config, list_id, mod_id)?,
27 set: userlist_get_set_version(config, list_id, mod_id)? 31 set: userlist_get_set_version(config, list_id, mod_id)?,
28 }) 32 })
29 } 33 }
30} 34}
@@ -64,23 +68,36 @@ impl ExportList {
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)?; 76 list_ids = lists_get_all_ids(config)?;
70 } else { 77 } else {
71 list_ids.push(lists_get(config, &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 {
83 progress.set_message(format!("Export {}", list_id));
84 //TODO download option/ new download on import
75 lists.push(ExportList::from(config, &list_id, true)?); 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}
@@ -108,7 +125,10 @@ pub async fn import(config: &Cfg, file_str: &str, direct_download: bool) -> MLE<
108 125
109 let mut ver_ids = vec![]; 126 let mut ver_ids = vec![];
110 for id in exportlist.versions { 127 for id in exportlist.versions {
111 ver_ids.push(AddMod { id: IDSelector::VersionID(id.version), set_version: id.set} ); 128 ver_ids.push(AddMod {
129 id: IDSelector::VersionID(id.version),
130 set_version: id.set,
131 });
112 } 132 }
113 mod_add(config, ver_ids, list, direct_download).await?; 133 mod_add(config, ver_ids, list, direct_download).await?;
114 } 134 }
diff --git a/src/commands/list.rs b/src/commands/list.rs
index 52f14f2..b0a082d 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, lists_get_all_ids,
5 lists_version, lists_get_all_ids, 7 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)]
@@ -28,20 +30,35 @@ pub fn list_add(
28 modloader: &Modloader, 30 modloader: &Modloader,
29 directory: &str, 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<()> {
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
35 if !lists_get_all_ids(config)?.into_iter().any(|l| l == id) { 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 //TODO add logging 56 let p = ProgressBar::new_spinner();
44 lists_remove(config, id) 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(())
45} 62}
46 63
47///Changing the current lists version and updating it 64///Changing the current lists version and updating it
@@ -57,17 +74,20 @@ pub async fn list_version(
57 download: bool, 74 download: bool,
58 delete: bool, 75 delete: bool,
59) -> MLE<()> { 76) -> MLE<()> {
60 println!( 77 let p = ProgressBar::new_spinner();
78 p.set_style(ProgressStyle::with_template(STYLE_OPERATION).unwrap());
79 p.set_message(format!(
61 "Change version for list {} to minecraft version: {}", 80 "Change version for list {} to minecraft version: {}",
62 id, mc_version 81 id, mc_version
63 ); 82 ));
64 83
65 lists_version(config, id, &mc_version)?; 84 lists_version(config, id, &mc_version)?;
66 85
67 println!( 86 p.finish_with_message(format!(
68 "\nCheck for updates for new minecraft version in list {}", 87 "Changed version for list {} to minecraft version: {}",
69 id 88 id, mc_version
70 ); 89 ));
90
71 let list = lists_get(config, id)?; 91 let list = lists_get(config, id)?;
72 update(config, vec![list], true, download, delete).await 92 update(config, vec![list], true, download, delete).await
73} 93}
diff --git a/src/commands/modification.rs b/src/commands/modification.rs
index 8abf913..fdb70c7 100644
--- a/src/commands/modification.rs
+++ b/src/commands/modification.rs
@@ -1,23 +1,23 @@
1use std::{io::Write, collections::HashMap}; 1use std::collections::HashMap;
2 2
3use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; 3use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
4 4
5use crate::{ 5use crate::{
6 config::Cfg, 6 config::Cfg,
7 db::{ 7 db::{
8 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, mods_remove,
9 userlist_get_current_version, userlist_insert, userlist_remove, mods_get_info, 9 userlist_get_all_ids, userlist_get_current_version, userlist_insert, userlist_remove,
10 }, 10 },
11 error::{ErrorType, MLError, MLE}, 11 error::{ErrorType, MLError, MLE},
12 files::{delete_version, download_versions}, 12 files::{delete_version, download_versions},
13 modrinth::{extract_current_version, get_raw_versions, project, projects, versions, Version}, 13 modrinth::{extract_current_version, get_raw_versions, project, projects, versions, Version},
14 List, PROGRESS_CHARS, STYLE_BAR_POS, 14 List, PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION,
15}; 15};
16 16
17#[derive(Debug)] 17#[derive(Debug)]
18pub struct AddMod { 18pub struct AddMod {
19 pub id: IDSelector, 19 pub id: IDSelector,
20 pub set_version: bool 20 pub set_version: bool,
21} 21}
22 22
23#[derive(Debug, PartialEq, Eq)] 23#[derive(Debug, PartialEq, Eq)]
@@ -47,9 +47,13 @@ pub async fn mod_add(
47 47
48 let mut mod_ids: Vec<(String, bool)> = Vec::new(); 48 let mut mod_ids: Vec<(String, bool)> = Vec::new();
49 let mut ver_ids: Vec<(String, bool)> = Vec::new(); 49 let mut ver_ids: Vec<(String, bool)> = Vec::new();
50 50
51 let add_p = mp.add(ProgressBar::new(mods.len().try_into().unwrap())); 51 let add_p = mp.add(ProgressBar::new(mods.len().try_into().unwrap()));
52 add_p.set_style(ProgressStyle::with_template(STYLE_BAR_POS).unwrap().progress_chars(PROGRESS_CHARS)); 52 add_p.set_style(
53 ProgressStyle::with_template(STYLE_BAR_POS)
54 .unwrap()
55 .progress_chars(PROGRESS_CHARS),
56 );
53 add_p.set_message("Sort ids"); 57 add_p.set_message("Sort ids");
54 58
55 //"Sort" project ids from version ids to be able to handle them differently but in a batch 59 //"Sort" project ids from version ids to be able to handle them differently but in a batch
@@ -62,7 +66,7 @@ pub async fn mod_add(
62 } 66 }
63 67
64 add_p.set_message("Get infos"); 68 add_p.set_message("Get infos");
65 69
66 let mut projectinfo: Vec<ProjectInfo> = Vec::new(); 70 let mut projectinfo: Vec<ProjectInfo> = Vec::new();
67 if !mod_ids.is_empty() { 71 if !mod_ids.is_empty() {
68 projectinfo.append(&mut get_mod_infos(config, mod_ids, list.clone()).await?); 72 projectinfo.append(&mut get_mod_infos(config, mod_ids, list.clone()).await?);
@@ -80,11 +84,17 @@ pub async fn mod_add(
80 let mut downloadstack: Vec<Version> = Vec::new(); 84 let mut downloadstack: Vec<Version> = Vec::new();
81 85
82 //Adding each mod to the lists and downloadstack 86 //Adding each mod to the lists and downloadstack
83 let project_p = mp.insert_before(&add_p, ProgressBar::new(projectinfo.len().try_into().unwrap())); 87 let project_p = mp.insert_before(
84 project_p.set_style(ProgressStyle::with_template(STYLE_BAR_POS).unwrap().progress_chars(PROGRESS_CHARS)); 88 &add_p,
89 ProgressBar::new(projectinfo.len().try_into().unwrap()),
90 );
91 project_p.set_style(
92 ProgressStyle::with_template(STYLE_BAR_POS)
93 .unwrap()
94 .progress_chars(PROGRESS_CHARS),
95 );
85 96
86 for project in projectinfo { 97 for project in projectinfo {
87
88 project_p.set_message(format!("Add {}", project.title)); 98 project_p.set_message(format!("Add {}", project.title));
89 99
90 let current_version_id = if project.current_version.is_none() { 100 let current_version_id = if project.current_version.is_none() {
@@ -116,12 +126,7 @@ pub async fn mod_add(
116 Ok(..) => Ok(..), 126 Ok(..) => Ok(..),
117 }?; 127 }?;
118 128
119 match mods_insert( 129 match mods_insert(config, &project.mod_id, &project.slug, &project.title) {
120 config,
121 &project.mod_id,
122 &project.slug,
123 &project.title,
124 ) {
125 Err(e) => { 130 Err(e) => {
126 if e.to_string() == "SQL: UNIQUE constraint failed: mods.id" { 131 if e.to_string() == "SQL: UNIQUE constraint failed: mods.id" {
127 Ok(..) 132 Ok(..)
@@ -152,8 +157,11 @@ pub async fn mod_add(
152 Ok(()) 157 Ok(())
153} 158}
154 159
155async fn get_mod_infos(config: &Cfg, mod_ids: Vec<(String, bool)>, list: List) -> MLE<Vec<ProjectInfo>> { 160async fn get_mod_infos(
156 161 config: &Cfg,
162 mod_ids: Vec<(String, bool)>,
163 list: List,
164) -> MLE<Vec<ProjectInfo>> {
157 let mut setmap: HashMap<String, bool> = HashMap::new(); 165 let mut setmap: HashMap<String, bool> = HashMap::new();
158 166
159 let mut ids = vec![]; 167 let mut ids = vec![];
@@ -197,19 +205,15 @@ async fn get_mod_infos(config: &Cfg, mod_ids: Vec<(String, bool)>, list: List) -
197 .find(|v| v.id == current_id) 205 .find(|v| v.id == current_id)
198 .unwrap(), 206 .unwrap(),
199 ); 207 );
200 208
201 // match primary, if none? 209 // match primary, if none?
202 let files = current_version 210 let files = current_version.clone().ok_or("").unwrap().files;
203 .clone()
204 .ok_or("")
205 .unwrap()
206 .files;
207 211
208 file = match files.clone().into_iter().find(|f| f.primary) { 212 file = match files.clone().into_iter().find(|f| f.primary) {
209 Some(f) => f, 213 Some(f) => f,
210 None => { files[0].clone() } 214 None => files[0].clone(),
211 } 215 }
212 .url; 216 .url;
213 217
214 for ver in available_versions { 218 for ver in available_versions {
215 available_versions_vec.push(ver.id); 219 available_versions_vec.push(ver.id);
@@ -247,7 +251,6 @@ async fn get_mod_infos(config: &Cfg, mod_ids: Vec<(String, bool)>, list: List) -
247} 251}
248 252
249async fn get_ver_info(config: &Cfg, ver_ids: Vec<(String, bool)>) -> MLE<Vec<ProjectInfo>> { 253async fn get_ver_info(config: &Cfg, ver_ids: Vec<(String, bool)>) -> MLE<Vec<ProjectInfo>> {
250
251 let mut setmap: HashMap<String, bool> = HashMap::new(); 254 let mut setmap: HashMap<String, bool> = HashMap::new();
252 255
253 let mut ids = vec![]; 256 let mut ids = vec![];
@@ -271,15 +274,13 @@ async fn get_ver_info(config: &Cfg, ver_ids: Vec<(String, bool)>) -> MLE<Vec<Pro
271 for (i, project) in v_projects.into_iter().enumerate() { 274 for (i, project) in v_projects.into_iter().enumerate() {
272 let version = &v_versions[i]; 275 let version = &v_versions[i];
273 276
274 let files = version 277 let files = version.clone().files;
275 .clone()
276 .files;
277 278
278 let file = match files.clone().into_iter().find(|f| f.primary) { 279 let file = match files.clone().into_iter().find(|f| f.primary) {
279 Some(f) => f, 280 Some(f) => f,
280 None => { files[0].clone() } 281 None => files[0].clone(),
281 } 282 }
282 .url; 283 .url;
283 284
284 projectinfo.push(ProjectInfo { 285 projectinfo.push(ProjectInfo {
285 mod_id: String::from(&project.id), 286 mod_id: String::from(&project.id),
@@ -300,34 +301,31 @@ async fn get_ver_info(config: &Cfg, ver_ids: Vec<(String, bool)>) -> MLE<Vec<Pro
300/// * `config` - config struct 301/// * `config` - config struct
301/// * `id` - name, slug or id of the mod 302/// * `id` - name, slug or id of the mod
302/// * `list` - List struct 303/// * `list` - List struct
303pub fn mod_remove(config: &Cfg, id: &str, list: List) -> MLE<()> { 304pub fn mod_remove(config: &Cfg, id: &str, list: &List) -> MLE<()> {
305 let progress = ProgressBar::new_spinner();
306 progress.set_style(ProgressStyle::with_template(STYLE_OPERATION).unwrap());
307
304 let mod_id = mods_get_id(&config.data, id)?; 308 let mod_id = mods_get_id(&config.data, id)?;
305 309
306 println!("Remove mod {} from {}", mods_get_info(config, &mod_id)?.title, list.id); 310 let info = mods_get_info(config, &mod_id)?;
311
312 progress.set_message(format!("Remove {} from {}", info.title, list.id));
313
307 let version = userlist_get_current_version(config, &list.id, &mod_id)?; 314 let version = userlist_get_current_version(config, &list.id, &mod_id)?;
308 315
309 print!(" â””Remove from list");
310 //Force flush of stdout, else print! doesn't print instantly
311 std::io::stdout().flush()?;
312 userlist_remove(config, &list.id, &mod_id)?; 316 userlist_remove(config, &list.id, &mod_id)?;
313 println!(" ✓");
314 317
315 print!(" â””Delete file"); 318 progress.set_message("Delete file");
316 //Force flush of stdout, else print! doesn't print instantly
317 std::io::stdout().flush()?;
318 match delete_version(list, version) { 319 match delete_version(list, version) {
319 Ok(_) => (), 320 Ok(_) => (),
320 Err(err) => { 321 Err(err) => {
321 if err.to_string() != "User input not accepted: VERSION_NOT_FOUND_IN_FILES" { 322 if err.to_string() != "User input not accepted: VERSION_NOT_FOUND_IN_FILES" {
322 return Err(err); 323 return Err(err);
323 }; 324 };
324 }, 325 }
325 }; 326 };
326 println!(" ✓");
327 327
328 print!(" â””Clean main db table"); 328 progress.set_message("Check main list");
329 //Force flush of stdout, else print! doesn't print instantly
330 std::io::stdout().flush()?;
331 let list_ids = lists_get_all_ids(config)?; 329 let list_ids = lists_get_all_ids(config)?;
332 330
333 // Remove mod from main list if not used elsewhere 331 // Remove mod from main list if not used elsewhere
@@ -336,11 +334,11 @@ pub fn mod_remove(config: &Cfg, id: &str, list: List) -> MLE<()> {
336 let mods = match userlist_get_all_ids(config, &id) { 334 let mods = match userlist_get_all_ids(config, &id) {
337 Ok(m) => m, 335 Ok(m) => m,
338 Err(err) => { 336 Err(err) => {
339 if err.to_string() == "Database: NO_MODS_USERLIST" { 337 if err.to_string() == "Database: NO_MODS_USERLIST" {
340 println!(" ✓"); 338 println!(" ✓");
341 return Ok(()); 339 return Ok(());
342 }; 340 };
343 return Err(err) 341 return Err(err);
344 } 342 }
345 }; 343 };
346 if mods.contains(&mod_id) { 344 if mods.contains(&mod_id) {
@@ -350,9 +348,11 @@ pub fn mod_remove(config: &Cfg, id: &str, list: List) -> MLE<()> {
350 } 348 }
351 349
352 if !mod_used { 350 if !mod_used {
353 mods_remove(config, mod_id)?; 351 progress.set_message("Remove from main list");
352 mods_remove(config, &mod_id)?;
354 }; 353 };
355 println!(" ✓"); 354
355 progress.finish_with_message(format!("Removed {} from {}", info.title, list.id));
356 356
357 Ok(()) 357 Ok(())
358} 358}
diff --git a/src/commands/update.rs b/src/commands/update.rs
index 194bbe5..3aae002 100644
--- a/src/commands/update.rs
+++ b/src/commands/update.rs
@@ -1,4 +1,4 @@
1use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; 1use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
2 2
3use crate::{ 3use crate::{
4 config::Cfg, 4 config::Cfg,
@@ -19,11 +19,14 @@ pub async fn update(
19 direct_download: bool, 19 direct_download: bool,
20 delete_old: bool, 20 delete_old: bool,
21) -> MLE<()> { 21) -> MLE<()> {
22
23 let mp = MultiProgress::new(); 22 let mp = MultiProgress::new();
24 23
25 let update_p = mp.add(ProgressBar::new(liststack.len().try_into().unwrap())); 24 let update_p = mp.add(ProgressBar::new(liststack.len().try_into().unwrap()));
26 update_p.set_style(ProgressStyle::with_template(STYLE_BAR_POS).unwrap().progress_chars(PROGRESS_CHARS)); 25 update_p.set_style(
26 ProgressStyle::with_template(STYLE_BAR_POS)
27 .unwrap()
28 .progress_chars(PROGRESS_CHARS),
29 );
27 30
28 for current_list in liststack { 31 for current_list in liststack {
29 update_p.set_message(format!("Update {}", current_list.id)); 32 update_p.set_message(format!("Update {}", current_list.id));
@@ -35,7 +38,11 @@ pub async fn update(
35 let mods = userlist_get_all_ids(config, &current_list.id)?; 38 let mods = userlist_get_all_ids(config, &current_list.id)?;
36 39
37 let list_u_p = mp.insert_before(&list_p, ProgressBar::new(mods.len().try_into().unwrap())); 40 let list_u_p = mp.insert_before(&list_p, ProgressBar::new(mods.len().try_into().unwrap()));
38 list_u_p.set_style(ProgressStyle::with_template(STYLE_BAR_POS).unwrap().progress_chars(PROGRESS_CHARS)); 41 list_u_p.set_style(
42 ProgressStyle::with_template(STYLE_BAR_POS)
43 .unwrap()
44 .progress_chars(PROGRESS_CHARS),
45 );
39 46
40 let mut current_versions: Vec<(String, String)> = vec![]; 47 let mut current_versions: Vec<(String, String)> = vec![];
41 let mut updatestack: Vec<Version> = vec![]; 48 let mut updatestack: Vec<Version> = vec![];
@@ -43,7 +50,7 @@ pub async fn update(
43 for id in mods { 50 for id in mods {
44 let info = mods_get_info(config, &id)?; 51 let info = mods_get_info(config, &id)?;
45 list_u_p.set_message(format!("Update {}", info.title)); 52 list_u_p.set_message(format!("Update {}", info.title));
46 53
47 //Skip check if version is set 54 //Skip check if version is set
48 if userlist_get_set_version(config, &current_list.id, &id)? { 55 if userlist_get_set_version(config, &current_list.id, &id)? {
49 list_u_p.inc(1); 56 list_u_p.inc(1);
@@ -51,19 +58,10 @@ pub async fn update(
51 } 58 }
52 59
53 //Getting current installed version for disable or delete 60 //Getting current installed version for disable or delete
54 let disable_version = 61 let disable_version = userlist_get_current_version(config, &current_list.id, &id)?;
55 userlist_get_current_version(config, &current_list.id, &id)?;
56 62
57 updatestack.push( 63 updatestack.push(
58 match specific_update( 64 match specific_update(config, clean, current_list.clone(), &id, &list_u_p).await {
59 config,
60 clean,
61 current_list.clone(),
62 &id,
63 &list_u_p
64 )
65 .await
66 {
67 Ok(ver) => { 65 Ok(ver) => {
68 current_versions.push((disable_version, id)); 66 current_versions.push((disable_version, id));
69 ver 67 ver
@@ -89,17 +87,31 @@ pub async fn update(
89 }; 87 };
90 88
91 if direct_download && !updatestack.is_empty() { 89 if direct_download && !updatestack.is_empty() {
92 download_versions(current_list.clone(), config.clone(), updatestack, &mp, &list_p).await?; 90 download_versions(
91 current_list.clone(),
92 config.clone(),
93 updatestack,
94 &mp,
95 &list_p,
96 )
97 .await?;
93 98
94 //Disable old versions 99 //Disable old versions
95 if !clean { 100 if !clean {
96 let d_p = mp.insert_before(&list_p, ProgressBar::new(current_versions.len().try_into().unwrap())); 101 let d_p = mp.insert_before(
97 d_p.set_style(ProgressStyle::with_template(STYLE_BAR_POS).unwrap().progress_chars(PROGRESS_CHARS)); 102 &list_p,
103 ProgressBar::new(current_versions.len().try_into().unwrap()),
104 );
105 d_p.set_style(
106 ProgressStyle::with_template(STYLE_BAR_POS)
107 .unwrap()
108 .progress_chars(PROGRESS_CHARS),
109 );
98 for ver in current_versions { 110 for ver in current_versions {
99 if delete_old { 111 if delete_old {
100 d_p.set_message(format!("Delete version {}", ver.0)); 112 d_p.set_message(format!("Delete version {}", ver.0));
101 d_p.inc(1); 113 d_p.inc(1);
102 delete_version(current_list.clone(), ver.0)?; 114 delete_version(&current_list, ver.0)?;
103 } else if ver.0 != "NONE" { 115 } else if ver.0 != "NONE" {
104 d_p.set_message(format!("Disable version {}", ver.0)); 116 d_p.set_message(format!("Disable version {}", ver.0));
105 d_p.inc(1); 117 d_p.inc(1);
@@ -125,9 +137,14 @@ pub async fn update(
125 Ok(()) 137 Ok(())
126} 138}
127 139
128async fn specific_update(config: &Cfg, clean: bool, list: List, id: &str, progress: &ProgressBar) -> MLE<Version> { 140async fn specific_update(
129 let applicable_versions = 141 config: &Cfg,
130 versions(&config.apis.modrinth, String::from(id), list.clone()).await; 142 clean: bool,
143 list: List,
144 id: &str,
145 progress: &ProgressBar,
146) -> MLE<Version> {
147 let applicable_versions = versions(&config.apis.modrinth, String::from(id), list.clone()).await;
131 148
132 let mut versions: Vec<String> = vec![]; 149 let mut versions: Vec<String> = vec![];
133 150
@@ -142,15 +159,16 @@ async fn specific_update(config: &Cfg, clean: bool, list: List, id: &str, progre
142 let mut current: Vec<Version> = vec![]; 159 let mut current: Vec<Version> = vec![];
143 if clean 160 if clean
144 || (versions.join("|") 161 || (versions.join("|")
145 != userlist_get_applicable_versions( 162 != userlist_get_applicable_versions(config, String::from(&list.id), String::from(id))?)
146 config,
147 String::from(&list.id),
148 String::from(id),
149 )?)
150 { 163 {
151 let current_str = extract_current_version(applicable_versions.clone())?; 164 let current_str = extract_current_version(applicable_versions.clone())?;
152 165
153 if !clean { progress.println(format!("Found new version for {}", mods_get_info(config, id).unwrap().title)); } 166 if !clean {
167 progress.println(format!(
168 "Found new version for {}",
169 mods_get_info(config, id).unwrap().title
170 ));
171 }
154 172
155 //get new versions 173 //get new versions
156 let current_ver = match applicable_versions 174 let current_ver = match applicable_versions
@@ -166,12 +184,19 @@ async fn specific_update(config: &Cfg, clean: bool, list: List, id: &str, progre
166 let files = &current_ver.files; 184 let files = &current_ver.files;
167 185
168 let link = match files.clone().into_iter().find(|f| f.primary) { 186 let link = match files.clone().into_iter().find(|f| f.primary) {
169 Some(f) => f, 187 Some(f) => f,
170 None => { files[0].clone() } 188 None => files[0].clone(),
171 } 189 }
172 .url; 190 .url;
173 191
174 userlist_change_versions(config, list.id, current_str, versions.join("|"), link, id.to_string())?; 192 userlist_change_versions(
193 config,
194 list.id,
195 current_str,
196 versions.join("|"),
197 link,
198 id.to_string(),
199 )?;
175 } 200 }
176 201
177 if current.is_empty() { 202 if current.is_empty() {
diff --git a/src/config.rs b/src/config.rs
index 54cf768..3858484 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -7,7 +7,7 @@ use std::{
7use indicatif::{ProgressBar, ProgressStyle}; 7use indicatif::{ProgressBar, ProgressStyle};
8use serde::{Deserialize, Serialize}; 8use serde::{Deserialize, Serialize};
9 9
10use crate::{db::db_setup, error::MLE, Modloader, VersionLevel, check_game_versions}; 10use crate::{check_game_versions, db::db_setup, error::MLE, Modloader, VersionLevel};
11 11
12#[derive(Debug, Clone, Serialize, Deserialize)] 12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Cfg { 13pub struct Cfg {
@@ -72,7 +72,7 @@ impl Cfg {
72 Err(..) => { 72 Err(..) => {
73 create_versions_dummy(&versionfile).await?; 73 create_versions_dummy(&versionfile).await?;
74 check_game_versions(&versionfile, true).await?; 74 check_game_versions(&versionfile, true).await?;
75 }, 75 }
76 } 76 }
77 77
78 Ok(config) 78 Ok(config)
@@ -95,7 +95,7 @@ fn create_config(path: &str) -> MLE<()> {
95 versions: cache_dir, 95 versions: cache_dir,
96 defaults: Defaults { 96 defaults: Defaults {
97 modloader: Modloader::Fabric, 97 modloader: Modloader::Fabric,
98 version: VersionLevel::Release 98 version: VersionLevel::Release,
99 }, 99 },
100 apis: Apis { 100 apis: Apis {
101 modrinth: String::from("https://api.modrinth.com/v2/"), 101 modrinth: String::from("https://api.modrinth.com/v2/"),
diff --git a/src/db.rs b/src/db.rs
index 1f3ad4c..f627ef4 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -120,9 +120,7 @@ pub fn mods_get_info(config: &Cfg, id: &str) -> MLE<ModInfo> {
120 } 120 }
121} 121}
122 122
123pub fn mods_remove(config: &Cfg, id: String) -> MLE<()> { 123pub fn mods_remove(config: &Cfg, id: &str) -> MLE<()> {
124 // println!("Removing mod {} from database", id);
125
126 let data = format!("{}/data.db", config.data); 124 let data = format!("{}/data.db", config.data);
127 let connection = Connection::open(data)?; 125 let connection = Connection::open(data)?;
128 126
@@ -233,7 +231,10 @@ pub fn userlist_get_all_ids(config: &Cfg, list_id: &str) -> MLE<Vec<String>> {
233 } 231 }
234 232
235 match mod_ids.is_empty() { 233 match mod_ids.is_empty() {
236 true => Err(MLError::new(ErrorType::DBError, &format!("NO_MODS_USERLIST{}", list_id))), 234 true => Err(MLError::new(
235 ErrorType::DBError,
236 &format!("NO_MODS_USERLIST{}", list_id),
237 )),
237 false => Ok(mod_ids), 238 false => Ok(mod_ids),
238 } 239 }
239} 240}
@@ -325,10 +326,7 @@ pub fn userlist_get_current_version(config: &Cfg, list_id: &str, mod_id: &str) -
325 } 326 }
326} 327}
327 328
328pub fn userlist_get_all_current_version_ids( 329pub fn userlist_get_all_current_version_ids(config: &Cfg, list_id: String) -> MLE<Vec<String>> {
329 config: &Cfg,
330 list_id: String,
331) -> MLE<Vec<String>> {
332 let data = format!("{}/data.db", config.data); 330 let data = format!("{}/data.db", config.data);
333 let connection = Connection::open(data)?; 331 let connection = Connection::open(data)?;
334 332
@@ -342,10 +340,7 @@ pub fn userlist_get_all_current_version_ids(
342 } 340 }
343 341
344 if versions.is_empty() { 342 if versions.is_empty() {
345 return Err(MLError::new( 343 return Err(MLError::new(ErrorType::DBError, "NO_MODS_ON_LIST"));
346 ErrorType::DBError,
347 "NO_MODS_ON_LIST",
348 ));
349 }; 344 };
350 345
351 Ok(versions) 346 Ok(versions)
@@ -441,7 +436,11 @@ pub fn userlist_add_disabled_versions(
441 Ok(()) 436 Ok(())
442} 437}
443 438
444pub fn userlist_get_disabled_versions(config: &Cfg, list_id: String, mod_id: String) -> MLE<String> { 439pub fn userlist_get_disabled_versions(
440 config: &Cfg,
441 list_id: String,
442 mod_id: String,
443) -> MLE<String> {
445 let data = format!("{}/data.db", config.data); 444 let data = format!("{}/data.db", config.data);
446 let connection = Connection::open(data).unwrap(); 445 let connection = Connection::open(data).unwrap();
447 446
@@ -504,19 +503,14 @@ pub fn lists_insert(
504 503
505 connection.execute( 504 connection.execute(
506 "INSERT INTO lists VALUES (?1, ?2, ?3, ?4)", 505 "INSERT INTO lists VALUES (?1, ?2, ?3, ?4)",
507 [ 506 [id, mc_version, &mod_loader.to_string(), download_folder],
508 id,
509 mc_version,
510 &mod_loader.to_string(),
511 download_folder,
512 ],
513 )?; 507 )?;
514 connection.execute(format!("CREATE TABLE {}( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'current_download' TEXT, 'disabled_versions' TEXT DEFAULT 'NONE', 'set_version' INTEGER, CONSTRAINT {}_PK PRIMARY KEY (mod_id) )", id, id).as_str(), [])?; 508 connection.execute(format!("CREATE TABLE {}( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'current_download' TEXT, 'disabled_versions' TEXT DEFAULT 'NONE', 'set_version' INTEGER, CONSTRAINT {}_PK PRIMARY KEY (mod_id) )", id, id).as_str(), [])?;
515 509
516 Ok(()) 510 Ok(())
517} 511}
518 512
519pub fn lists_remove(config: &Cfg, id: String) -> MLE<()> { 513pub fn lists_remove(config: &Cfg, id: &str) -> MLE<()> {
520 let data = format!("{}/data.db", config.data); 514 let data = format!("{}/data.db", config.data);
521 let connection = Connection::open(data)?; 515 let connection = Connection::open(data)?;
522 516
@@ -593,7 +587,7 @@ pub fn lists_get_all_ids(config: &Cfg) -> MLE<Vec<String>> {
593} 587}
594 588
595//config 589//config
596pub fn config_change_current_list(config: &Cfg, id: String) -> MLE<()> { 590pub fn config_change_current_list(config: &Cfg, id: &str) -> MLE<()> {
597 let data = format!("{}/data.db", config.data); 591 let data = format!("{}/data.db", config.data);
598 let connection = Connection::open(data)?; 592 let connection = Connection::open(data)?;
599 593
@@ -655,7 +649,10 @@ pub fn s_config_create_version(config: &Cfg) -> Result<(), Box<dyn std::error::E
655 Ok(()) 649 Ok(())
656} 650}
657 651
658pub fn s_config_update_version(config: &Cfg, ver: String) -> Result<(), Box<dyn std::error::Error>> { 652pub fn s_config_update_version(
653 config: &Cfg,
654 ver: String,
655) -> Result<(), Box<dyn std::error::Error>> {
659 let data = format!("{}/data.db", config.data); 656 let data = format!("{}/data.db", config.data);
660 let connection = Connection::open(data)?; 657 let connection = Connection::open(data)?;
661 658
diff --git a/src/error.rs b/src/error.rs
index e6afeaa..f981f14 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -106,9 +106,11 @@ impl From<std::io::Error> for MLError {
106 106
107impl From<serde_json::error::Error> for MLError { 107impl From<serde_json::error::Error> for MLError {
108 fn from(value: serde_json::error::Error) -> Self { 108 fn from(value: serde_json::error::Error) -> Self {
109 Self { etype: ErrorType::LibJson, message: value.to_string() } 109 Self {
110 etype: ErrorType::LibJson,
111 message: value.to_string(),
112 }
110 } 113 }
111
112} 114}
113 115
114impl MLError { 116impl MLError {
diff --git a/src/files.rs b/src/files.rs
index 814f06d..e874d9d 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -1,12 +1,13 @@
1use futures_util::StreamExt; 1use futures_util::StreamExt;
2use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; 2use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
3use reqwest::Client; 3use reqwest::Client;
4use tokio::task::JoinSet;
5use std::{ 4use std::{
5 cmp::min,
6 collections::HashMap, 6 collections::HashMap,
7 fs::{copy, read_dir, remove_file, rename, File}, 7 fs::{copy, read_dir, remove_file, rename, File},
8 io::Write, cmp::min, 8 io::Write,
9}; 9};
10use tokio::task::JoinSet;
10 11
11use crate::{ 12use crate::{
12 cache::{copy_cached_version, get_cached_versions}, 13 cache::{copy_cached_version, get_cached_versions},
@@ -14,34 +15,61 @@ use crate::{
14 db::{mods_get_info, userlist_add_disabled_versions}, 15 db::{mods_get_info, userlist_add_disabled_versions},
15 error::{ErrorType, MLError, MLE}, 16 error::{ErrorType, MLError, MLE},
16 modrinth::Version, 17 modrinth::Version,
17 List, PROGRESS_CHARS, STYLE_SPINNER, STYLE_BAR_BYTE, STYLE_BAR_POS, 18 List, PROGRESS_CHARS, STYLE_BAR_BYTE, STYLE_BAR_POS, STYLE_SPINNER,
18}; 19};
19 20
20pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>, progress: &MultiProgress, progress_before: &ProgressBar) -> MLE<()> { 21pub async fn download_versions(
22 list: List,
23 config: Cfg,
24 versions: Vec<Version>,
25 progress: &MultiProgress,
26 progress_before: &ProgressBar,
27) -> MLE<()> {
21 let cached = get_cached_versions(&config.cache); 28 let cached = get_cached_versions(&config.cache);
22 29
23 let mut js = JoinSet::new(); 30 let mut js = JoinSet::new();
24 31
25 let style_spinner = ProgressStyle::with_template(STYLE_SPINNER).unwrap(); 32 let style_spinner = ProgressStyle::with_template(STYLE_SPINNER).unwrap();
26 33
27 let all = progress.insert_before(progress_before, ProgressBar::new(versions.len().try_into().unwrap())); 34 let all = progress.insert_before(
28 all.set_style(ProgressStyle::with_template(STYLE_BAR_POS).unwrap().progress_chars(PROGRESS_CHARS)); 35 progress_before,
36 ProgressBar::new(versions.len().try_into().unwrap()),
37 );
38 all.set_style(
39 ProgressStyle::with_template(STYLE_BAR_POS)
40 .unwrap()
41 .progress_chars(PROGRESS_CHARS),
42 );
29 all.set_message(format!("✓Downloading {}", list.id)); 43 all.set_message(format!("✓Downloading {}", list.id));
30 44
31 for ver in versions { 45 for ver in versions {
32 let p = progress.insert_before(&all, ProgressBar::new(1)); 46 let p = progress.insert_before(&all, ProgressBar::new(1));
33 p.set_style(style_spinner.clone()); 47 p.set_style(style_spinner.clone());
34 js.spawn(download_version(config.clone(), list.clone(), ver, cached.clone(), p)); 48 js.spawn(download_version(
49 config.clone(),
50 list.clone(),
51 ver,
52 cached.clone(),
53 p,
54 ));
55 }
56
57 while js.join_next().await.is_some() {
58 all.inc(1)
35 } 59 }
36 60
37 while js.join_next().await.is_some() { all.inc(1) }
38
39 all.finish_with_message(format!("✓Downloading {}", list.id)); 61 all.finish_with_message(format!("✓Downloading {}", list.id));
40 62
41 Ok(()) 63 Ok(())
42} 64}
43 65
44async fn download_version(config: Cfg, list: List, version: Version, mut cached: HashMap<String, String>, progress: ProgressBar) -> MLE<()> { 66async fn download_version(
67 config: Cfg,
68 list: List,
69 version: Version,
70 mut cached: HashMap<String, String>,
71 progress: ProgressBar,
72) -> MLE<()> {
45 let project_info = mods_get_info(&config, &version.project_id)?; 73 let project_info = mods_get_info(&config, &version.project_id)?;
46 74
47 let dl_path = String::from(&list.download_folder); 75 let dl_path = String::from(&list.download_folder);
@@ -59,7 +87,7 @@ async fn download_version(config: Cfg, list: List, version: Version, mut cached:
59 let files = version.files; 87 let files = version.files;
60 let file = match files.clone().into_iter().find(|f| f.primary) { 88 let file = match files.clone().into_iter().find(|f| f.primary) {
61 Some(f) => f, 89 Some(f) => f,
62 None => files[0].clone() 90 None => files[0].clone(),
63 }; 91 };
64 let mut splitname: Vec<&str> = file.filename.split('.').collect(); 92 let mut splitname: Vec<&str> = file.filename.split('.').collect();
65 let extension = match splitname.pop().ok_or("") { 93 let extension = match splitname.pop().ok_or("") {
@@ -74,13 +102,7 @@ async fn download_version(config: Cfg, list: List, version: Version, mut cached:
74 extension 102 extension
75 ); 103 );
76 104
77 download_file( 105 download_file(&file.url, &list.download_folder, &filename, &progress).await?;
78 &file.url,
79 &list.download_folder,
80 &filename,
81 &progress
82 )
83 .await?;
84 106
85 progress.set_message(format!("Copy {} to cache", version.id)); 107 progress.set_message(format!("Copy {} to cache", version.id));
86 let dl_path_file = format!("{}/{}", list.download_folder, filename); 108 let dl_path_file = format!("{}/{}", list.download_folder, filename);
@@ -89,7 +111,10 @@ async fn download_version(config: Cfg, list: List, version: Version, mut cached:
89 copy(dl_path_file, cache_path)?; 111 copy(dl_path_file, cache_path)?;
90 } 112 }
91 113
92 progress.finish_with_message(format!("✓{} - {}{}", project_info.title, version.id, cache_msg)); 114 progress.finish_with_message(format!(
115 "✓{} - {}{}",
116 project_info.title, version.id, cache_msg
117 ));
93 118
94 Ok(()) 119 Ok(())
95} 120}
@@ -117,7 +142,7 @@ async fn download_file(url: &str, path: &str, name: &str, progress: &ProgressBar
117 // progress.inc(1); 142 // progress.inc(1);
118 let chunk = item?; 143 let chunk = item?;
119 file.write_all(&chunk)?; 144 file.write_all(&chunk)?;
120 145
121 // Progress bar 146 // Progress bar
122 let new = min(downloaded + (chunk.len() as u64), size); 147 let new = min(downloaded + (chunk.len() as u64), size);
123 downloaded = new; 148 downloaded = new;
@@ -136,7 +161,7 @@ pub fn disable_version(
136 mod_id: String, 161 mod_id: String,
137) -> MLE<()> { 162) -> MLE<()> {
138 //println!("Disabling version {} for mod {}", versionid, mod_id); 163 //println!("Disabling version {} for mod {}", versionid, mod_id);
139 let file = get_file_path(current_list.clone(), String::from(&versionid))?; 164 let file = get_file_path(&current_list, String::from(&versionid))?;
140 let disabled = format!("{}.disabled", file); 165 let disabled = format!("{}.disabled", file);
141 166
142 rename(file, disabled)?; 167 rename(file, disabled)?;
@@ -146,7 +171,7 @@ pub fn disable_version(
146 Ok(()) 171 Ok(())
147} 172}
148 173
149pub fn delete_version(list: List, version: String) -> MLE<()> { 174pub fn delete_version(list: &List, version: String) -> MLE<()> {
150 let file = get_file_path(list, version)?; 175 let file = get_file_path(list, version)?;
151 176
152 remove_file(file)?; 177 remove_file(file)?;
@@ -154,9 +179,9 @@ pub fn delete_version(list: List, version: String) -> MLE<()> {
154 Ok(()) 179 Ok(())
155} 180}
156 181
157pub fn get_file_path(list: List, versionid: String) -> MLE<String> { 182pub fn get_file_path(list: &List, versionid: String) -> MLE<String> {
158 let mut names: HashMap<String, String> = HashMap::new(); 183 let mut names: HashMap<String, String> = HashMap::new();
159 for file in read_dir(list.download_folder)? { 184 for file in read_dir(&list.download_folder)? {
160 let path = file?.path(); 185 let path = file?.path();
161 if path.is_file() { 186 if path.is_file() {
162 let pathstr = match path.to_str().ok_or("") { 187 let pathstr = match path.to_str().ok_or("") {
diff --git a/src/lib.rs b/src/lib.rs
index 7287660..8196f1c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,20 +6,26 @@ pub mod db;
6pub mod error; 6pub mod error;
7pub mod files; 7pub mod files;
8 8
9use std::{fmt::Display, fs::{File, remove_file, self}, io::{Write, Read}, time::Duration}; 9use std::{
10 fmt::Display,
11 fs::{self, remove_file, File},
12 io::{Read, Write},
13 time::Duration,
14};
10 15
11pub use apis::*;
12use apis::modrinth::{get_game_versions, GameVersion, GameVersionType}; 16use apis::modrinth::{get_game_versions, GameVersion, GameVersionType};
17pub use apis::*;
13pub use commands::*; 18pub use commands::*;
14use error::{ErrorType, MLError, MLE}; 19use error::{ErrorType, MLError, MLE};
15use indicatif::{ProgressStyle, ProgressBar}; 20use indicatif::{ProgressBar, ProgressStyle};
16use serde::{Deserialize, Serialize}; 21use serde::{Deserialize, Serialize};
17 22
18pub static STYLE_BAR_BYTE: &str = "{spinner:.green}{wide_msg}{bytes}/{total_bytes} [{bar:.green/lime}]"; 23pub static STYLE_BAR_BYTE: &str =
24 "{spinner:.green}{wide_msg}{bytes}/{total_bytes} [{bar:.green/lime}]";
19pub static STYLE_BAR_POS: &str = " {wide_msg}{pos}/{len} [{bar:.green/lime}]"; 25pub static STYLE_BAR_POS: &str = " {wide_msg}{pos}/{len} [{bar:.green/lime}]";
20pub static STYLE_SPINNER: &str = "{spinner:.green}{wide_msg}"; 26pub static STYLE_SPINNER: &str = "{spinner:.green}{wide_msg}";
21pub static STYLE_OPERATION: &str = " {wide_msg}"; 27pub static STYLE_OPERATION: &str = " {wide_msg}";
22pub static STYLE_MESSAGE: &str = "{wide_msg}"; 28pub static STYLE_MESSAGE: &str = "{wide_msg}";
23pub static PROGRESS_CHARS: &str = "#>-"; 29pub static PROGRESS_CHARS: &str = "#>-";
24 30
25#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] 31#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
@@ -59,7 +65,7 @@ pub enum VersionLevel {
59 Release, 65 Release,
60 #[serde(rename(serialize = "snapshot", deserialize = "snapshot"))] 66 #[serde(rename(serialize = "snapshot", deserialize = "snapshot"))]
61 Snapshot, 67 Snapshot,
62 Version(String) 68 Version(String),
63} 69}
64 70
65/// Checks if update needed (time) 71/// Checks if update needed (time)
@@ -70,7 +76,9 @@ pub async fn check_game_versions(path: &str, force: bool) -> MLE<()> {
70 p.set_message("Update minecraft versions"); 76 p.set_message("Update minecraft versions");
71 77
72 let creation_time = fs::metadata(path)?.created()?; 78 let creation_time = fs::metadata(path)?.created()?;
73 if !force && creation_time.elapsed().unwrap() < Duration::from_secs(60 * 60 * 24) { return Ok(()); } 79 if !force && creation_time.elapsed().unwrap() < Duration::from_secs(60 * 60 * 24) {
80 return Ok(());
81 }
74 82
75 let versions = get_game_versions().await; 83 let versions = get_game_versions().await;
76 remove_file(path)?; 84 remove_file(path)?;
@@ -91,7 +99,6 @@ pub fn load_game_versions(path: &str) -> MLE<Vec<GameVersion>> {
91} 99}
92 100
93impl VersionLevel { 101impl VersionLevel {
94
95 pub fn from(str: &str) -> Self { 102 pub fn from(str: &str) -> Self {
96 match str { 103 match str {
97 "release" => VersionLevel::Release, 104 "release" => VersionLevel::Release,
@@ -107,22 +114,29 @@ impl VersionLevel {
107 114
108 match self { 115 match self {
109 VersionLevel::Release => { 116 VersionLevel::Release => {
110 let release = versions.find(|ver| ver.version_type == GameVersionType::release).unwrap(); 117 let release = versions
118 .find(|ver| ver.version_type == GameVersionType::release)
119 .unwrap();
111 println!("{:?}", release); 120 println!("{:?}", release);
112 Ok(release.version) 121 Ok(release.version)
113 }, 122 }
114 VersionLevel::Snapshot => { 123 VersionLevel::Snapshot => {
115 let snapshot = versions.find(|ver| ver.version_type == GameVersionType::snapshot).unwrap(); 124 let snapshot = versions
125 .find(|ver| ver.version_type == GameVersionType::snapshot)
126 .unwrap();
116 println!("{:?}", snapshot); 127 println!("{:?}", snapshot);
117 Ok(snapshot.version) 128 Ok(snapshot.version)
118 }, 129 }
119 VersionLevel::Version(v) => { 130 VersionLevel::Version(v) => {
120 if versions.any(|ver| ver.version == v) { 131 if versions.any(|ver| ver.version == v) {
121 Ok(v) 132 Ok(v)
122 } else { 133 } else {
123 Err(MLError::new(ErrorType::ConfigError, "unknown minecraft version")) 134 Err(MLError::new(
135 ErrorType::ConfigError,
136 "unknown minecraft version",
137 ))
124 } 138 }
125 }, 139 }
126 } 140 }
127 } 141 }
128} 142}
diff --git a/src/main.rs b/src/main.rs
index d03f88a..d9ad6af 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,8 +2,8 @@ use clap::{Parser, Subcommand};
2use modlist::{ 2use modlist::{
3 config::Cfg, 3 config::Cfg,
4 db::{config_get_current_list, lists_get, lists_get_all_ids}, 4 db::{config_get_current_list, lists_get, lists_get_all_ids},
5 download, export, get_current_list, import, list_add, list_change, list_remove, list_version, 5 download, export, get_current_list, import, list_add, list_change, list_list, list_remove,
6 mod_add, mod_remove, update, IDSelector, List, Modloader, VersionLevel, list_list, AddMod, 6 list_version, mod_add, mod_remove, update, AddMod, IDSelector, List, Modloader, VersionLevel,
7}; 7};
8 8
9#[derive(Parser)] 9#[derive(Parser)]
@@ -43,7 +43,7 @@ enum Commands {
43 /// remove disabled versions 43 /// remove disabled versions
44 #[arg(short, long)] 44 #[arg(short, long)]
45 remove: bool, 45 remove: bool,
46 46
47 /// optional List selection, else default list will be used 47 /// optional List selection, else default list will be used
48 #[arg(short, long)] 48 #[arg(short, long)]
49 list: Option<String>, 49 list: Option<String>,
@@ -81,7 +81,7 @@ enum Commands {
81 /// the list you want to export 81 /// the list you want to export
82 list: Option<String>, 82 list: Option<String>,
83 }, 83 },
84 Test 84 Test,
85} 85}
86 86
87#[derive(Subcommand)] 87#[derive(Subcommand)]
@@ -160,86 +160,87 @@ async fn main() {
160 let config = Cfg::init(cli.config).await.unwrap(); 160 let config = Cfg::init(cli.config).await.unwrap();
161 161
162 match cli.command { 162 match cli.command {
163 Commands::Mod { command } => { 163 Commands::Mod { command } => match command {
164 match command { 164 ModCommands::Add {
165 ModCommands::Add { 165 id,
166 id, 166 version,
167 version, 167 list,
168 list, 168 download,
169 download, 169 lock,
170 lock, 170 } => {
171 } => { 171 let listf = match list {
172 let listf = match list { 172 Some(list) => lists_get(&config, &list).unwrap(),
173 Some(list) => lists_get(&config, &list).unwrap(), 173 None => lists_get(&config, &config_get_current_list(&config).unwrap()).unwrap(),
174 None => lists_get( 174 };
175 &config,
176 &config_get_current_list(&config).unwrap(),
177 )
178 .unwrap(),
179 };
180 175
181 let marked_id = match version { 176 let marked_id = match version {
182 true => IDSelector::VersionID(id), 177 true => IDSelector::VersionID(id),
183 false => IDSelector::ModificationID(id), 178 false => IDSelector::ModificationID(id),
184 }; 179 };
185 180
186 let add_id = AddMod { id: marked_id, set_version: lock }; 181 let add_id = AddMod {
182 id: marked_id,
183 set_version: lock,
184 };
187 185
188 mod_add(&config, vec![add_id], listf, download).await 186 mod_add(&config, vec![add_id], listf, download).await
189 }
190 ModCommands::Remove { id, list } => {
191 let listf = match list {
192 Some(list) => lists_get(&config, &list).unwrap(),
193 None => lists_get(
194 &config,
195 &config_get_current_list(&config).unwrap(),
196 )
197 .unwrap(),
198 };
199 mod_remove(&config, &id, listf)
200 }
201 } 187 }
202 } 188 ModCommands::Remove { id, list } => {
203 Commands::List { command, force_gameupdate } => { 189 let listf = match list {
204 match command { 190 Some(list) => lists_get(&config, &list).unwrap(),
205 ListCommands::Add { 191 None => lists_get(&config, &config_get_current_list(&config).unwrap()).unwrap(),
206 id, 192 };
207 directory, 193 mod_remove(&config, &id, &listf)
208 modloader,
209 version,
210 } => {
211 let ml = match modloader {
212 Some(ml) => Modloader::from(&ml).unwrap(),
213 None => config.defaults.modloader.clone(),
214 };
215
216 let versions_path = &config.versions;
217 let ver = match version {
218 Some(ver) => VersionLevel::from(&ver).get(versions_path, force_gameupdate).await.unwrap(),
219 None => config.defaults.version.clone().get(versions_path, force_gameupdate).await.unwrap(),
220 };
221
222 list_add(&config, &id, &ver, &ml, &directory)
223 }
224 ListCommands::Remove { id } => list_remove(&config, id),
225 ListCommands::List => {
226 list_list(&config)
227 }
228 ListCommands::Change { id } => list_change(&config, id),
229 ListCommands::Version {
230 id,
231 version,
232 download,
233 remove,
234 } => list_version(&config, &id, version, download, remove).await,
235 } 194 }
236 } 195 },
196 Commands::List {
197 command,
198 force_gameupdate,
199 } => match command {
200 ListCommands::Add {
201 id,
202 directory,
203 modloader,
204 version,
205 } => {
206 let ml = match modloader {
207 Some(ml) => Modloader::from(&ml).unwrap(),
208 None => config.defaults.modloader.clone(),
209 };
210
211 let versions_path = &config.versions;
212 let ver = match version {
213 Some(ver) => VersionLevel::from(&ver)
214 .get(versions_path, force_gameupdate)
215 .await
216 .unwrap(),
217 None => config
218 .defaults
219 .version
220 .clone()
221 .get(versions_path, force_gameupdate)
222 .await
223 .unwrap(),
224 };
225
226 list_add(&config, &id, &ver, &ml, &directory)
227 }
228 ListCommands::Remove { id } => list_remove(&config, &id),
229 ListCommands::List => list_list(&config),
230 ListCommands::Change { id } => list_change(&config, &id),
231 ListCommands::Version {
232 id,
233 version,
234 download,
235 remove,
236 } => list_version(&config, &id, version, download, remove).await,
237 },
237 Commands::Update { 238 Commands::Update {
238 all, 239 all,
239 download, 240 download,
240 clean, 241 clean,
241 remove, 242 remove,
242 list 243 list,
243 } => { 244 } => {
244 let mut liststack: Vec<List> = vec![]; 245 let mut liststack: Vec<List> = vec![];
245 if all { 246 if all {
@@ -248,7 +249,7 @@ async fn main() {
248 liststack.push(lists_get(&config, &id).unwrap()); 249 liststack.push(lists_get(&config, &id).unwrap());
249 } 250 }
250 } else { 251 } else {
251 let current = match list { 252 let current = match list {
252 Some(l) => lists_get(&config, &l).unwrap(), 253 Some(l) => lists_get(&config, &l).unwrap(),
253 None => get_current_list(&config).unwrap(), 254 None => get_current_list(&config).unwrap(),
254 }; 255 };
@@ -257,7 +258,12 @@ async fn main() {
257 258
258 update(&config, liststack, clean, download, remove).await 259 update(&config, liststack, clean, download, remove).await
259 } 260 }
260 Commands::Download { all, clean, remove, list } => { 261 Commands::Download {
262 all,
263 clean,
264 remove,
265 list,
266 } => {
261 let mut liststack: Vec<List> = vec![]; 267 let mut liststack: Vec<List> = vec![];
262 if all { 268 if all {
263 let list_ids = lists_get_all_ids(&config).unwrap(); 269 let list_ids = lists_get_all_ids(&config).unwrap();
@@ -265,15 +271,15 @@ async fn main() {
265 liststack.push(lists_get(&config, &id).unwrap()); 271 liststack.push(lists_get(&config, &id).unwrap());
266 } 272 }
267 } else { 273 } else {
268 let current = match list { 274 let current = match list {
269 Some(l) => lists_get(&config, &l).unwrap(), 275 Some(l) => lists_get(&config, &l).unwrap(),
270 None => get_current_list(&config).unwrap(), 276 None => get_current_list(&config).unwrap(),
271 }; 277 };
272 liststack.push(current) 278 liststack.push(current)
273 } 279 }
274 280
275 download(&config, liststack, clean, remove).await 281 download(&config, liststack, clean, remove).await
276 }, 282 }
277 Commands::Import { file, download } => { 283 Commands::Import { file, download } => {
278 let filestr: String = match file { 284 let filestr: String = match file {
279 Some(args) => args, 285 Some(args) => args,