From 529d52534c300aec4a6e3e9e08f9762a401f7086 Mon Sep 17 00:00:00 2001 From: fxqnlr Date: Thu, 25 May 2023 11:16:16 +0200 Subject: added more progress --- src/apis/modrinth.rs | 2 +- src/cache.rs | 1 - src/commands/download.rs | 4 +-- src/commands/io.rs | 2 +- src/commands/list.rs | 2 +- src/commands/modification.rs | 70 +++++++++++++++++++++++++++------------ src/commands/update.rs | 79 ++++++++++++++++++++++++++++++++------------ src/config.rs | 4 +-- src/db.rs | 2 +- src/files.rs | 61 ++++++++++++++++------------------ src/lib.rs | 6 ++-- 11 files changed, 145 insertions(+), 88 deletions(-) (limited to 'src') diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs index 525cc0d..14ff266 100644 --- a/src/apis/modrinth.rs +++ b/src/apis/modrinth.rs @@ -170,7 +170,7 @@ pub async fn projects(api: &str, ids: Vec) -> Vec { pub async fn versions(api: &str, id: String, list: List) -> Vec { let url = format!( r#"project/{}/version?loaders=["{}"]&game_versions=["{}"]"#, - id, list.modloader.to_string(), list.mc_version + id, list.modloader, list.mc_version ); let data = get(api, &url).await.unwrap(); diff --git a/src/cache.rs b/src/cache.rs index c928670..1e22091 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -32,6 +32,5 @@ pub fn get_cached_versions(path: &str) -> HashMap { pub fn copy_cached_version(version_path: &str, download_path: &str) { let versplit: Vec<&str> = version_path.split('/').collect(); let download = format!("{}/{}", download_path, versplit[versplit.len() - 1]); - // println!("{:#?}", download); copy(version_path, download).unwrap(); } diff --git a/src/commands/download.rs b/src/commands/download.rs index ebfb4eb..6831714 100644 --- a/src/commands/download.rs +++ b/src/commands/download.rs @@ -1,3 +1,4 @@ + use crate::{config::Cfg, List}; use crate::{ db::userlist_get_all_current_versions_with_mods, @@ -9,7 +10,6 @@ use crate::{ }; pub async fn download(config: Cfg, liststack: Vec, clean: bool, delete_old: bool) -> MLE<()> { - for current_list in liststack { println!("Downloading current versions of mods in {}", current_list.id); let downloaded_versions = get_downloaded_versions(current_list.clone())?; @@ -63,7 +63,7 @@ pub async fn download(config: Cfg, liststack: Vec, clean: bool, delete_old if !to_disable.is_empty() { for ver in to_disable { if delete_old { - println!("Deleting version {} for mod {}", ver.1, ver.0); + // println!("Deleting version {} for mod {}", ver.1, ver.0); delete_version(current_list.clone(), ver.1)?; } else { disable_version(config.clone(), current_list.clone(), ver.1, ver.0)?; diff --git a/src/commands/io.rs b/src/commands/io.rs index dd294bc..2a26f1d 100644 --- a/src/commands/io.rs +++ b/src/commands/io.rs @@ -24,7 +24,7 @@ impl ExportVersion { fn from(config: Cfg, list_id: &str, mod_id: &str) -> MLE { Ok(Self { version: userlist_get_current_version(config.clone(), list_id, mod_id)?, - set: userlist_get_set_version(config.clone(), list_id, mod_id)? + set: userlist_get_set_version(config, list_id, mod_id)? }) } } diff --git a/src/commands/list.rs b/src/commands/list.rs index 4aa4306..c07823b 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -32,7 +32,7 @@ pub fn list_add( } pub fn list_change(config: Cfg, id: String) -> MLE<()> { - if lists_get_all_ids(config.clone())?.into_iter().find(|l| l == &id).is_none() { + if !lists_get_all_ids(config.clone())?.into_iter().any(|l| l == id) { return Err(MLError::new(ErrorType::ArgumentError, "List not found")); }; println!("Change default list to: {}", id); diff --git a/src/commands/modification.rs b/src/commands/modification.rs index d4c49d6..730583d 100644 --- a/src/commands/modification.rs +++ b/src/commands/modification.rs @@ -1,5 +1,7 @@ use std::{io::Write, collections::HashMap}; +use indicatif::{ProgressBar, ProgressStyle}; + use crate::{ config::Cfg, db::{ @@ -12,6 +14,8 @@ use crate::{ List, }; +const PROGRESS_CHARS: &str = "#>-"; + #[derive(Debug, Clone)] pub struct AddMod { pub id: IDSelector, @@ -41,28 +45,45 @@ pub async fn mod_add( list: List, direct_download: bool, ) -> MLE<()> { - println!("Add mods to {}", list.id); - println!(" └Add mods:"); + let spinner_style = ProgressStyle::with_template("{spinner:.green}{msg}").unwrap(); + let bar_style = ProgressStyle::with_template("{spinner:.green}{wide_msg}{pos}/{len} [{bar:.green/lime}]").unwrap().progress_chars(PROGRESS_CHARS); + + // println!("Add mods to {}", list.id); + // println!(" └Add mods:"); let mut mod_ids: Vec<(String, bool)> = Vec::new(); let mut ver_ids: Vec<(String, bool)> = Vec::new(); + let p = ProgressBar::new(mods.len().try_into().unwrap()); + p.set_style(spinner_style.clone()); + p.set_message("Sort ids"); + //"Sort" project ids from version ids to be able to handle them differently but in a batch for m in mods { + p.inc(1); match m.id { IDSelector::ModificationID(pid) => mod_ids.push((pid, m.set_version)), IDSelector::VersionID(vid) => ver_ids.push((vid, m.set_version)), } } - + + p.finish_with_message("Sort ids done"); + + let info_p = ProgressBar::new(2); + info_p.set_message("Get infos"); + info_p.set_style(bar_style.clone()); let mut projectinfo: Vec = Vec::new(); if !mod_ids.is_empty() { - projectinfo.append(&mut get_mod_infos(config.clone(), mod_ids, list.clone()).await?) + projectinfo.append(&mut get_mod_infos(config.clone(), mod_ids, list.clone()).await?); + info_p.inc(1); }; if !ver_ids.is_empty() { - projectinfo.append(&mut get_ver_info(config.clone(), ver_ids).await?) + projectinfo.append(&mut get_ver_info(config.clone(), ver_ids).await?); + info_p.inc(1); }; + info_p.finish_with_message("Get infos done"); + if projectinfo.is_empty() { return Err(MLError::new(ErrorType::ArgumentError, "NO_IDS?")); }; @@ -70,18 +91,19 @@ pub async fn mod_add( let mut downloadstack: Vec = Vec::new(); //Adding each mod to the lists and downloadstack - if projectinfo.len() == 1 { - println!(" └Insert mod in list {} and save infos", list.id); - } else { - println!(" └Insert mods in list {} and save infos", list.id); - } + let add_p = ProgressBar::new(projectinfo.len().try_into().unwrap()); + add_p.set_style(bar_style); for project in projectinfo { + + add_p.set_message(format!("Add {}", project.title)); + let current_version_id = if project.current_version.is_none() { String::from("NONE") } else { project.current_version.clone().unwrap().id }; + match userlist_insert( config.clone(), &list.id, @@ -124,8 +146,13 @@ pub async fn mod_add( if project.current_version.is_some() { downloadstack.push(project.current_version.unwrap()) }; + + // add_p.println(format!("Added {}", project.title)); + add_p.inc(1); } + add_p.finish_with_message("Added all mods"); + //Download all the added mods if direct_download { download_versions(list.clone(), config.clone(), downloadstack).await?; @@ -140,7 +167,7 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) -> let mut ids = vec![]; - println!("{:?}", mod_ids); + // println!("{:?}", mod_ids); for id in mod_ids { setmap.insert(id.0.to_string(), id.1); @@ -156,8 +183,8 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) -> _ => panic!("PANIC"), }; for project in m_projects { - println!("\t└{}", project.title); - println!("\t └Get versions"); + // println!("\t└{}", project.title); + // println!("\t └Get versions"); let available_versions = versions( &config.apis.modrinth, String::from(&project.id), @@ -170,7 +197,7 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) -> let file: String; if !available_versions.is_empty() { let current_id = extract_current_version(available_versions.clone())?; - println!("\t └Current version: {}", current_id); + // println!("\t └Current version: {}", current_id); current_version = Some( available_versions @@ -197,7 +224,7 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) -> available_versions_vec.push(ver.id); } - println!("{:?}", setmap); + // println!("{:?}", setmap); projectinfo.push(ProjectInfo { mod_id: String::from(&project.id), @@ -206,10 +233,10 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) -> current_version, applicable_versions: available_versions_vec, download_link: file, - set_version: setmap.get(&project.slug).unwrap().clone(), + set_version: *setmap.get(&project.slug).unwrap(), }) } else { - println!("\t └There's currently no mod version for your specified target"); + // println!("\t └There's currently no mod version for your specified target"); current_version = None; file = String::from("NONE"); available_versions_vec.push(String::from("NONE")); @@ -220,7 +247,7 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<(String, bool)>, list: List) -> current_version, applicable_versions: available_versions_vec, download_link: file, - set_version: setmap.get(&project.id).unwrap().clone(), + set_version: *setmap.get(&project.id).unwrap(), }) } } @@ -252,7 +279,7 @@ async fn get_ver_info(config: Cfg, ver_ids: Vec<(String, bool)>) -> MLE) -> MLE) -> MLE MLE<()> { let mod_id = mods_get_id(&config.data, id)?; - println!("Remove mod {} from {}", mods_get_info(config.clone(), &mod_id)?.title, list.id); + println!("Remove mod {} from {}", mods_get_info(&config, &mod_id)?.title, list.id); let version = userlist_get_current_version(config.clone(), &list.id, &mod_id)?; print!(" └Remove from list"); @@ -300,7 +327,6 @@ pub fn mod_remove(config: Cfg, id: &str, list: List) -> MLE<()> { if err.to_string() != "User input not accepted: VERSION_NOT_FOUND_IN_FILES" { return Err(err); }; - () }, }; println!(" ✓"); diff --git a/src/commands/update.rs b/src/commands/update.rs index d3a282b..2de13f3 100644 --- a/src/commands/update.rs +++ b/src/commands/update.rs @@ -1,3 +1,5 @@ +use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; + use crate::{ config::Cfg, db::{ @@ -10,6 +12,8 @@ use crate::{ List, }; +const PROGRESS_CHARS: &str = "#>-"; + pub async fn update( config: Cfg, liststack: Vec, @@ -17,20 +21,39 @@ pub async fn update( direct_download: bool, delete_old: bool, ) -> MLE<()> { + + let mp = MultiProgress::new(); + + let update_p = mp.add(ProgressBar::new(liststack.len().try_into().unwrap())); + let bar_style = ProgressStyle::with_template("{spinner:.green}{wide_msg}{pos}/{len} [{bar:.green/lime}]").unwrap().progress_chars(PROGRESS_CHARS); + let spinner_style = ProgressStyle::with_template("{spinner:.green}{msg}").unwrap(); + update_p.set_style(bar_style.clone()); + update_p.set_message("Update"); + for current_list in liststack { - println!("Update mods in {}", current_list.id); + + // println!("Update mods in {}", current_list.id); let mods = userlist_get_all_ids(config.clone(), ¤t_list.id)?; + let list_p = mp.insert_before(&update_p, ProgressBar::new(mods.len().try_into().unwrap())); + list_p.set_style(bar_style.clone()); + list_p.set_message(format!("Update {}", current_list.id)); + let mut current_versions: Vec<(String, String)> = vec![]; let mut updatestack: Vec = vec![]; for id in mods { - let info = mods_get_info(config.clone(), &id)?; - println!(" ├{}", info.title); + let mod_p = mp.insert_before(&list_p, ProgressBar::new(1)); + mod_p.set_style(spinner_style.clone()); + + let info = mods_get_info(&config, &id)?; + mod_p.set_message(format!("Update {}", info.title)); + // println!(" ├{}", info.title); if userlist_get_set_version(config.clone(), ¤t_list.id, &id)? { - println!(" │ └Set version, skipping update"); + // println!(" │ └Set version, skipping update"); + list_p.inc(1); continue; } @@ -38,12 +61,15 @@ pub async fn update( let disable_version = userlist_get_current_version(config.clone(), ¤t_list.id, &id)?; + mod_p.inc(1); + updatestack.push( match specific_update( config.clone(), clean, current_list.clone(), String::from(&id), + &mod_p ) .await { @@ -53,19 +79,25 @@ pub async fn update( } Err(e) => { if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" { - println!( - " │ └No new version found for the specified minecraft version" - ); + // println!( + // " │ └No new version found for the specified minecraft version" + // ); } else { return Err(e); }; + list_p.inc(1); continue; } }, - ) + ); + list_p.inc(1); } + list_p.finish_with_message(format!("Updated {}", current_list.id)); + if clean { + update_p.set_message("Cleaning"); + update_p.inc(1); clean_list_dir(¤t_list)?; }; @@ -85,12 +117,16 @@ pub async fn update( } } }; + update_p.inc(1); } + update_p.finish_with_message("Updated all lists"); + + Ok(()) } -async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> MLE { +async fn specific_update(config: Cfg, clean: bool, list: List, id: String, progress: &ProgressBar) -> MLE { let applicable_versions = versions(&config.apis.modrinth, String::from(&id), list.clone()).await; @@ -116,10 +152,11 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML let current_str = extract_current_version(applicable_versions.clone())?; if clean { - println!("\t └Add version to downloadstack"); + // println!("\t └Add version to downloadstack"); } else { - println!("\t └Get versions for specified minecraft versions"); - println!("\t └New current version: {}", current_str); + progress.println(format!("Found new version for {}", mods_get_info(&config, &id).unwrap().title)); + // println!("\t └Get versions for specified minecraft versions"); + // println!("\t └New current version: {}", current_str); }; //get new versions @@ -133,16 +170,14 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML }?; current.push(current_ver.clone()); - let link = match current_ver - .files - .into_iter() - .find(|f| f.primary) - .ok_or("!no primary in links") - { - Ok(p) => Ok(p), - Err(e) => Err(MLError::new(ErrorType::Other, e)), - }? - .url; + let files = ¤t_ver.files; + + let link = match files.clone().into_iter().find(|f| f.primary) { + Some(f) => f, + None => { files[0].clone() } + } + .url; + userlist_change_versions(config, list.id, current_str, versions.join("|"), link, id)?; } diff --git a/src/config.rs b/src/config.rs index e1049d1..0cb1891 100644 --- a/src/config.rs +++ b/src/config.rs @@ -31,7 +31,7 @@ pub struct Defaults { impl Cfg { pub async fn init(path: Option) -> MLE { let configfile = match path.clone() { - Some(p) => String::from(p), + Some(p) => p, None => dirs::config_dir() .unwrap() .join("modlist") @@ -89,7 +89,7 @@ fn create_config(path: &str) -> MLE<()> { let default_cfg = Cfg { data: cache_dir.clone(), cache: format!("{}/cache", cache_dir), - versions: cache_dir.clone(), + versions: cache_dir, defaults: Defaults { modloader: Modloader::Fabric, version: VersionLevel::Release diff --git a/src/db.rs b/src/db.rs index 8fd21b1..dde00ab 100644 --- a/src/db.rs +++ b/src/db.rs @@ -93,7 +93,7 @@ pub struct ModInfo { pub title: String, } -pub fn mods_get_info(config: Cfg, id: &str) -> MLE { +pub fn mods_get_info(config: &Cfg, id: &str) -> MLE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; diff --git a/src/files.rs b/src/files.rs index a4a1d3b..04b00f0 100644 --- a/src/files.rs +++ b/src/files.rs @@ -17,55 +17,50 @@ use crate::{ List, }; +const PROGRESS_CHARS: &str = "#>-"; + pub async fn download_versions(list: List, config: Cfg, versions: Vec) -> MLE<()> { let cached = get_cached_versions(&config.cache); - // println!("{:#?}", cached); - - // println!(" └Download mods to {}", dl_path); - let mp = MultiProgress::new(); let mut js = JoinSet::new(); - let style = ProgressStyle::with_template("{spinner:.green}{msg}\t[{bar:.green/lime}] {bytes}/{total_bytes}") - .unwrap() - .progress_chars("#>-"); + let style_spinner = ProgressStyle::with_template("{spinner:.green}{wide_msg}").unwrap(); + + let all = mp.add(ProgressBar::new(versions.len().try_into().unwrap())); + all.set_style(ProgressStyle::with_template("{wide_msg}{pos}/{len} [{bar:.green/lime}]").unwrap().progress_chars(PROGRESS_CHARS)); + all.set_message("Downloading"); + for ver in versions { - let p = mp.add(ProgressBar::new(1)); - p.set_style(style.clone()); + let p = mp.insert_before(&all, ProgressBar::new(1)); + p.set_style(style_spinner.clone()); js.spawn(download_version(config.clone(), list.clone(), ver, cached.clone(), p)); + // std::thread::sleep(std::time::Duration::from_millis(200)); } - mp.clear().unwrap(); + while js.join_next().await.is_some() { all.inc(1) } + + all.finish(); - while js.join_next().await.is_some() {} + // mp.clear().unwrap(); Ok(()) } async fn download_version(config: Cfg, list: List, version: Version, mut cached: HashMap, progress: ProgressBar) -> MLE<()> { - let project_info = mods_get_info(config.clone(), &version.project_id)?; + let project_info = mods_get_info(&config, &version.project_id)?; let dl_path = String::from(&list.download_folder); - progress.set_message(String::from(&version.id)); + progress.set_message(format!("{} - {}", project_info.title, version.id)); //Check cache if already downloaded let c = cached.remove(&version.id); if c.is_some() { - print!( - "\t└({})Get version {} from cache", - project_info.title, version.id - ); - //Force flush of stdout, else print! doesn't print instantly - std::io::stdout().flush()?; + progress.set_message(format!("Get {} from cache", version.id)); copy_cached_version(&c.unwrap(), &dl_path); - println!(" ✓"); } else { - // print!("\t└({})Download version {}", project_info.title, version.id); - //Force flush of stdout, else print! doesn't print instantly - std::io::stdout().flush().unwrap(); let files = version.files; let file = match files.clone().into_iter().find(|f| f.primary) { Some(f) => f, @@ -91,19 +86,15 @@ async fn download_version(config: Cfg, list: List, version: Version, mut cached: &progress ) .await?; - // println!(" ✓"); - //Copy file to cache - // print!("\t └Copy to cache"); - //Force flush of stdout, else print! doesn't print instantly - std::io::stdout().flush().unwrap(); + + progress.set_message(format!("Copy {} to cache", version.id)); let dl_path_file = format!("{}/{}", list.download_folder, filename); let cache_path = format!("{}/{}", &config.clone().cache, filename); - // println!("{}:{}", dl_path_file, cache_path); + copy(dl_path_file, cache_path)?; - // println!(" ✓"); } - progress.finish_with_message(format!("✓{}", version.id)); + progress.finish_with_message(format!("✓{} - {}", project_info.title, version.id)); Ok(()) } @@ -114,8 +105,12 @@ async fn download_file(url: &str, path: &str, name: &str, progress: &ProgressBar let size = res.content_length().expect("Couldn't get content length"); + let style_bar_byte = ProgressStyle::with_template("{spinner:.green}{wide_msg}{bytes}/{total_bytes} [{bar:.green/lime}]") + .unwrap() + .progress_chars(PROGRESS_CHARS); + progress.set_length(size); - // progress.set_style(ProgressStyle::with_template("{spinner:.green}{msg}\t[{wide_bar:.green/lime}] {bytes}/{total_bytes}").unwrap().progress_chars("#>-")); + progress.set_style(style_bar_byte); // download chunks let mut file = File::create(&dl_path_file)?; @@ -124,7 +119,7 @@ async fn download_file(url: &str, path: &str, name: &str, progress: &ProgressBar let mut downloaded: u64 = 0; while let Some(item) = stream.next().await { - progress.inc(1); + // progress.inc(1); let chunk = item?; file.write_all(&chunk)?; diff --git a/src/lib.rs b/src/lib.rs index 1c40ceb..f59ba89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,8 @@ pub use commands::*; use error::{ErrorType, MLError, MLE}; use serde::{Deserialize, Serialize}; +pub static TICK_CHARS: &str = "#>-"; + #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub enum Modloader { #[serde(rename(serialize = "fabric", deserialize = "fabric"))] @@ -64,7 +66,7 @@ pub async fn check_game_versions(path: &str, force: bool) -> MLE<()> { let versions = get_game_versions().await; remove_file(path)?; let mut file = File::create(path)?; - file.write_all(&serde_json::to_string_pretty(&versions)?.as_bytes())?; + file.write_all(serde_json::to_string_pretty(&versions)?.as_bytes())?; println!(" ✓"); Ok(()) } @@ -105,7 +107,7 @@ impl VersionLevel { Ok(snapshot.version) }, VersionLevel::Version(v) => { - if versions.find(|ver| ver.version == v).is_some() { + if versions.any(|ver| ver.version == v) { Ok(v) } else { Err(MLError::new(ErrorType::ConfigError, "unknown minecraft version")) -- cgit v1.2.3