diff options
author | fx <[email protected]> | 2023-05-29 18:02:08 +0200 |
---|---|---|
committer | fx <[email protected]> | 2023-05-29 18:02:08 +0200 |
commit | d3870a2efa74e68c643dfb4aef32edc2536503b0 (patch) | |
tree | 116075aaa57c35afca2749719d450c3cb473ab3e /src/commands/modification.rs | |
parent | 5a2ea0755b29a8811aeeec1c73679c5783082628 (diff) | |
parent | c7ecf3019a75dc0ab1a0aefeb9b880899fc8a231 (diff) | |
download | modlist-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.rs | 204 |
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 @@ | |||
1 | use std::{io::Write, collections::HashMap}; | 1 | use std::collections::HashMap; |
2 | |||
3 | use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; | ||
2 | 4 | ||
3 | use crate::{ | 5 | use 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)] |
16 | pub struct AddMod { | 22 | pub 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)] |
22 | pub enum IDSelector { | 28 | pub 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 | ||
38 | pub async fn mod_add( | 44 | pub 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 | ¤t_version_id, | 117 | ¤t_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 | ||
137 | async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) -> MLE<Vec<ProjectInfo>> { | 182 | async 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 | ||
227 | async fn get_ver_info(config: Cfg, ver_ids: Vec<(String, bool)>) -> MLE<Vec<ProjectInfo>> { | 268 | async 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 |
278 | pub fn mod_remove(config: Cfg, id: &str, list: List) -> MLE<()> { | 322 | pub 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 | } |