summaryrefslogtreecommitdiff
path: root/src/commands/modification.rs
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/modification.rs
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/modification.rs')
-rw-r--r--src/commands/modification.rs204
1 files changed, 125 insertions, 79 deletions
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}