From ecc4743fdec43eb578e9c35bb008c68909f1517e Mon Sep 17 00:00:00 2001 From: fxqnlr Date: Wed, 4 Sep 2024 17:32:19 +0200 Subject: better error handling --- Cargo.lock | 9 +-- Cargo.toml | 1 + src/apis/modrinth.rs | 51 +++++--------- src/cache.rs | 12 ++-- src/commands/download.rs | 44 +++++------- src/commands/io.rs | 29 ++++---- src/commands/list.rs | 38 ++++------ src/commands/modification.rs | 63 ++++++++--------- src/commands/update.rs | 160 ++++++++++++++++++++----------------------- src/config.rs | 26 ++++--- src/data.rs | 2 +- src/data/gameversion.rs | 33 ++++----- src/data/list.rs | 4 +- src/data/modloader.rs | 14 ++-- src/data/project.rs | 12 ---- src/data/projectinfo.rs | 12 ++++ src/db.rs | 151 ++++++++++++++++++++-------------------- src/error.rs | 129 ---------------------------------- src/errors.rs | 132 +++++++++++++++++++++++++++++++++++ src/files.rs | 54 +++++++++------ src/main.rs | 6 +- 21 files changed, 469 insertions(+), 513 deletions(-) delete mode 100644 src/data/project.rs create mode 100644 src/data/projectinfo.rs delete mode 100644 src/error.rs create mode 100644 src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index 5b301ef..fee095b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -957,6 +957,7 @@ dependencies = [ "rusqlite", "serde", "serde_json", + "thiserror", "tokio", "toml 0.7.4", ] @@ -1527,18 +1528,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 39e73c8..db65be5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ clap = { version = "4.2.1", features = ["derive"] } clap_complete = "4.2.0" indicatif = "0.17.3" config = "0.14.0" +thiserror = "1.0.63" diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs index 7f1fb52..afd7b69 100644 --- a/src/apis/modrinth.rs +++ b/src/apis/modrinth.rs @@ -3,7 +3,7 @@ use reqwest::Client; use serde::{Deserialize, Serialize}; use crate::{ - error::{EType, MLErr, MLE}, + errors::{Error, MLE}, List, }; @@ -131,10 +131,7 @@ pub enum GameVersionType { } /// # Errors -async fn get( - api: &str, - path: &str, -) -> Result>, Box> { +async fn get(api: &str, path: &str) -> Result, Error> { let url = format!(r#"{api}{path}"#); let client = Client::builder() @@ -145,23 +142,19 @@ async fn get( .build()?; let res = client.get(url).send().await?; - let mut data: Option> = None; - - if res.status() == 200 { - data = Some(res.bytes().await?.to_vec()); + if res.status() != 200 { + return Err(Error::RequestNotOK(res.status())); } - Ok(data) + Ok(res.bytes().await?.to_vec()) } /// # Errors pub async fn project(api: &str, name: &str) -> MLE { let url = format!("project/{name}"); - let data = get(api, &url).await - .map_err(|_| MLErr::new(EType::Other, "geterr"))? - .ok_or(MLErr::new(EType::Other, "geterr2"))?; + let data = get(api, &url).await?; - serde_json::from_slice(&data).map_err(|_| MLErr::new(EType::LibJson, "from project")) + Ok(serde_json::from_slice(&data)?) } /// # Errors @@ -169,11 +162,9 @@ pub async fn projects(api: &str, ids: Vec) -> MLE> { let all = ids.join(r#"",""#); let url = format!(r#"projects?ids=["{all}"]"#); - let data = get(api, &url).await - .map_err(|_| MLErr::new(EType::Other, "geterr"))? - .ok_or(MLErr::new(EType::Other, "geterr2"))?; + let data = get(api, &url).await?; - serde_json::from_slice(&data).map_err(|_| MLErr::new(EType::LibJson, "from projects")) + Ok(serde_json::from_slice(&data)?) } ///Get applicable versions from `mod_id` with list context @@ -184,12 +175,12 @@ pub async fn versions(api: &str, id: String, list: List) -> MLE> { id, list.modloader, list.mc_version ); - let data = get(api, &url).await - .map_err(|_| MLErr::new(EType::Other, "geterr"))?; + let data = get(api, &url).await?; - Ok(match data { - Some(data) => serde_json::from_slice(&data).map_err(|_| MLErr::new(EType::LibJson, "from version"))?, - None => Vec::new(), + Ok(if data.is_empty() { + Vec::new() + } else { + serde_json::from_slice(&data)? }) } @@ -201,17 +192,15 @@ pub async fn get_raw_versions( ) -> MLE> { let url = format!(r#"versions?ids=["{}"]"#, versions.join(r#"",""#)); - let data = get(api, &url).await - .map_err(|_| MLErr::new(EType::Other, "geterr"))? - .ok_or(MLErr::new(EType::Other, "geterr2"))?; + let data = get(api, &url).await?; - serde_json::from_slice(&data).map_err(|_| MLErr::new(EType::LibJson, "from raw version")) + Ok(serde_json::from_slice(&data)?) } /// # Errors pub fn extract_current_version(versions: Vec) -> MLE { match versions.len() { - 0 => Err(MLErr::new(EType::ModError, "NO_VERSIONS_AVAILABLE")), + 0 => Err(Error::ModError("NO_VERSIONS_AVAILABLE".to_string())), 1.. => { let mut times: Vec<(String, DateTime)> = vec![]; for ver in versions { @@ -228,9 +217,7 @@ pub fn extract_current_version(versions: Vec) -> MLE { /// # Errors pub async fn get_game_versions() -> MLE> { let data = get("https://api.modrinth.com/v2/", "tag/game_version") - .await - .map_err(|_| MLErr::new(EType::Other, "geterr"))? - .ok_or(MLErr::new(EType::Other, "geterr2"))?; + .await?; - serde_json::from_slice(&data).map_err(|_| MLErr::new(EType::LibJson, "from game version")) + Ok(serde_json::from_slice(&data)?) } diff --git a/src/cache.rs b/src/cache.rs index 6ffeb52..199a440 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -3,15 +3,15 @@ use std::{ fs::{copy, read_dir}, }; -use crate::error::{EType, MLErr, MLE}; +use crate::errors::{ConversionError, Error, MLE}; /// # Errors pub fn get_cached_versions(path: &str) -> MLE> { let mut versions: HashMap = HashMap::new(); - for file in read_dir(path).map_err(|_| MLErr::new(EType::IoError, "readdir"))? { - let path = file.map_err(|_| MLErr::new(EType::IoError, "file"))?.path(); - if path.is_file() && path.extension().ok_or(MLErr::new(EType::IoError, "ext"))? == "jar" { - let pathstr = path.to_str().ok_or(MLErr::new(EType::IoError, "path"))?; + for file in read_dir(path)? { + let path = file?.path(); + if path.is_file() && path.extension().ok_or(Error::NoFileExtension)? == "jar" { + let pathstr = path.to_str().ok_or(ConversionError::InvalidPath)?; let namesplit: Vec<&str> = pathstr.split('.').collect(); versions.insert( String::from(namesplit[namesplit.len() - 2]), @@ -27,6 +27,6 @@ pub fn copy_cached_version(version_path: &str, download_path: &str) -> MLE<()> { let versplit: Vec<&str> = version_path.split('/').collect(); let download = format!("{}/{}", download_path, versplit[versplit.len() - 1]); - copy(version_path, download).map_err(|err| MLErr::new(EType::IoError, &err.to_string()))?; + copy(version_path, download)?; Ok(()) } diff --git a/src/commands/download.rs b/src/commands/download.rs index 269d5d3..bb946b0 100644 --- a/src/commands/download.rs +++ b/src/commands/download.rs @@ -1,10 +1,11 @@ use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use crate::apis::modrinth::get_raw_versions; +use crate::errors::Error; use crate::{config::Cfg, List}; use crate::{ db::userlist_get_all_current_versions_with_mods, - error::{EType, MLErr, MLE}, + errors::MLE, files::{ clean_list_dir, delete_version, disable_version, download_versions, get_downloaded_versions, @@ -20,20 +21,22 @@ pub async fn download( delete_old: bool, ) -> MLE<()> { let mp = MultiProgress::new(); - let download_p = mp.add(ProgressBar::new( - liststack - .len() - .try_into() - .map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?, - )); + let download_p = mp.add(ProgressBar::new(liststack.len().try_into()?)); download_p.set_style( - ProgressStyle::with_template(STYLE_BAR_POS) - .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))? + ProgressStyle::with_template(STYLE_BAR_POS)? .progress_chars(PROGRESS_CHARS), ); for current_list in liststack { - download_list(config, mp.clone(), download_p.clone(), current_list, clean, delete_old).await?; + download_list( + config, + mp.clone(), + download_p.clone(), + current_list, + clean, + delete_old, + ) + .await?; } download_p.finish_with_message("Downloaded all lists"); @@ -52,13 +55,8 @@ async fn download_list( download_p.set_message(format!("Download in {}", current_list.id)); let downloaded_versions = get_downloaded_versions(¤t_list)?; - let current_version_ids = match userlist_get_all_current_versions_with_mods( - config, - ¤t_list.id, - ) { - Ok(i) => Ok(i), - Err(e) => Err(MLErr::new(EType::DBError, e.to_string().as_str())), - }?; + let current_version_ids = + userlist_get_all_current_versions_with_mods(config, ¤t_list.id)?; let mut to_download: Vec = vec![]; //(mod_id, version_id) @@ -74,7 +72,7 @@ async fn download_list( to_download.push(current_version); } else { let downloaded_version = - current_download.ok_or(MLErr::new(EType::Other, "IDK, WTF"))?; + current_download.ok_or(Error::NoDownload)?; if ¤t_version != downloaded_version { to_disable .push((mod_id.clone(), String::from(downloaded_version))); @@ -106,16 +104,10 @@ async fn download_list( if !to_disable.is_empty() { let d_p = mp.insert_before( &download_p, - ProgressBar::new( - to_disable - .len() - .try_into() - .map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?, - ), + ProgressBar::new(to_disable.len().try_into()?), ); d_p.set_style( - ProgressStyle::with_template(STYLE_BAR_POS) - .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))? + ProgressStyle::with_template(STYLE_BAR_POS)? .progress_chars(PROGRESS_CHARS), ); for ver in to_disable { diff --git a/src/commands/io.rs b/src/commands/io.rs index dea0d84..80bc7d6 100644 --- a/src/commands/io.rs +++ b/src/commands/io.rs @@ -4,10 +4,14 @@ use std::fs::File; use std::io::prelude::*; use crate::{ - config::Cfg, data::modification::{AddMod, IDSelector}, db::{ + config::Cfg, + data::modification::{AddMod, IDSelector}, + db::{ lists_get, lists_get_all_ids, lists_insert, userlist_get_all_ids, userlist_get_current_version, userlist_get_set_version, - }, error::{EType, MLErr, MLE}, mod_add, List, Modloader, STYLE_OPERATION + }, + errors::{ConversionError, Error, MLE}, + mod_add, List, Modloader, STYLE_OPERATION, }; #[derive(Debug, Serialize, Deserialize)] @@ -67,22 +71,13 @@ impl ExportList { /// # Errors pub fn export(config: &Cfg, list: Option) -> MLE<()> { let progress = ProgressBar::new_spinner(); - progress.set_style( - ProgressStyle::with_template(STYLE_OPERATION) - .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?, - ); + progress.set_style(ProgressStyle::with_template(STYLE_OPERATION)?); let mut list_ids: Vec = vec![]; if list.is_none() { list_ids = lists_get_all_ids(config)?; } else { - list_ids.push( - lists_get( - config, - &list.ok_or(MLErr::new(EType::Other, "nolist"))?, - )? - .id, - ); + list_ids.push(lists_get(config, &list.ok_or(Error::ListNotFound)?)?.id); } let mut lists: Vec = vec![]; @@ -95,11 +90,11 @@ pub fn export(config: &Cfg, list: Option) -> MLE<()> { let toml = toml::to_string(&Export { lists })?; let filestr = dirs::home_dir() - .ok_or(MLErr::new(EType::Other, "no home"))? + .ok_or(Error::SysDirNotFound("home".to_string()))? .join("mlexport.toml") .into_os_string() .into_string() - .map_err(|_| MLErr::new(EType::IoError, "No String"))?; + .map_err(|_| ConversionError::InvalidPath)?; progress.set_message("Create file"); let mut file = File::create(&filestr)?; @@ -124,10 +119,10 @@ pub async fn import( let list = List { id: exportlist.id, mc_version: exportlist.mc_version, - modloader: Modloader::from(&exportlist.launcher)?, + modloader: Modloader::try_from(exportlist.launcher.as_str())?, download_folder: exportlist .download_folder - .ok_or(MLErr::new(EType::Other, "NO_DL"))?, + .ok_or(Error::NoDownloadFolder)?, }; lists_insert( config, diff --git a/src/commands/list.rs b/src/commands/list.rs index 23a9f0f..db8a831 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -1,10 +1,14 @@ use indicatif::{ProgressBar, ProgressStyle}; use crate::{ - config::Cfg, data::modloader::Modloader, db::{ - config_change_current_list, lists_get, - lists_get_all_ids, lists_insert, lists_remove, lists_version, - }, error::{EType, MLErr, MLE}, update, STYLE_OPERATION + config::Cfg, + data::modloader::Modloader, + db::{ + config_change_current_list, lists_get, lists_get_all_ids, lists_insert, + lists_remove, lists_version, + }, + errors::{Error, MLE}, + update, STYLE_OPERATION, }; /// # Errors @@ -16,11 +20,7 @@ pub fn add( directory: &str, ) -> MLE<()> { let p = ProgressBar::new_spinner(); - p.set_style( - ProgressStyle::with_template(STYLE_OPERATION).map_err(|_| { - MLErr::new(EType::LibIndicatif, "template error") - })?, - ); + p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?); p.set_message(format!("Create {id}")); lists_insert(config, id, mc_version, modloader, directory)?; p.finish_with_message(format!("Created {id}")); @@ -30,15 +30,11 @@ pub fn add( /// # Errors pub fn change(config: &Cfg, id: &str) -> MLE<()> { let p = ProgressBar::new_spinner(); - p.set_style( - ProgressStyle::with_template(STYLE_OPERATION).map_err(|_| { - MLErr::new(EType::LibIndicatif, "template error") - })?, - ); + p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?); p.set_message(format!("Change default list to {id}")); if !lists_get_all_ids(config)?.into_iter().any(|l| l == id) { - return Err(MLErr::new(EType::ArgumentError, "List not found")); + return Err(Error::ListNotFound); }; config_change_current_list(config, id)?; @@ -49,11 +45,7 @@ pub fn change(config: &Cfg, id: &str) -> MLE<()> { /// # Errors pub fn remove(config: &Cfg, id: &str) -> MLE<()> { let p = ProgressBar::new_spinner(); - p.set_style( - ProgressStyle::with_template(STYLE_OPERATION).map_err(|_| { - MLErr::new(EType::LibIndicatif, "template error") - })?, - ); + p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?); p.set_message(format!("Remove {id}")); lists_remove(config, id)?; p.finish_with_message(format!("Removed {id}")); @@ -75,11 +67,7 @@ pub async fn version( delete: bool, ) -> MLE<()> { let p = ProgressBar::new_spinner(); - p.set_style( - ProgressStyle::with_template(STYLE_OPERATION).map_err(|_| { - MLErr::new(EType::LibIndicatif, "template error") - })?, - ); + p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?); p.set_message(format!( "Change version for list {id} to minecraft version: {mc_version}" )); diff --git a/src/commands/modification.rs b/src/commands/modification.rs index d20f575..27ba098 100644 --- a/src/commands/modification.rs +++ b/src/commands/modification.rs @@ -3,11 +3,23 @@ use std::collections::HashMap; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use crate::{ - apis::modrinth::{extract_current_version, get_raw_versions, project, projects, versions, Version}, config::Cfg, data::{modification::{AddMod, IDSelector}, project::ProjectInfo}, db::{ + apis::modrinth::{ + extract_current_version, get_raw_versions, project, projects, versions, + Version, + }, + config::Cfg, + data::{ + modification::{AddMod, IDSelector}, + projectinfo::ProjectInfo, + }, + db::{ lists_get_all_ids, mods_get_id, mods_get_info, mods_insert, mods_remove, userlist_get_all_ids, userlist_get_current_version, userlist_insert, userlist_remove, - }, error::{EType, MLErr, MLE}, files::{delete_version, download_versions}, List, PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION + }, + errors::{Error, MLE}, + files::{delete_version, download_versions}, + List, PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION, }; /// # Errors @@ -22,14 +34,9 @@ pub async fn mod_add( let mut mod_ids: Vec<(String, bool)> = Vec::new(); let mut ver_ids: Vec<(String, bool)> = Vec::new(); - let add_p = mp.add(ProgressBar::new( - mods.len() - .try_into() - .map_err(|_| MLErr::new(EType::Other, "MODSLENTRY"))?, - )); + let add_p = mp.add(ProgressBar::new(mods.len().try_into()?)); add_p.set_style( - ProgressStyle::with_template(STYLE_BAR_POS) - .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))? + ProgressStyle::with_template(STYLE_BAR_POS)? .progress_chars(PROGRESS_CHARS), ); add_p.set_message("Sort ids"); @@ -57,7 +64,7 @@ pub async fn mod_add( }; if projectinfo.is_empty() { - return Err(MLErr::new(EType::ArgumentError, "NO_IDS?")); + return Err(Error::NoProjectInfo); }; add_p.set_message("Add mods to database"); @@ -65,29 +72,18 @@ pub async fn mod_add( let mut downloadstack: Vec = Vec::new(); //Adding each mod to the lists and downloadstack - let project_p = mp.insert_before( - &add_p, - ProgressBar::new( - projectinfo - .len() - .try_into() - .map_err(|_| MLErr::new(EType::Other, "infolen"))?, - ), - ); + let project_p = mp + .insert_before(&add_p, ProgressBar::new(projectinfo.len().try_into()?)); project_p.set_style( - ProgressStyle::with_template(STYLE_BAR_POS) - .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))? + ProgressStyle::with_template(STYLE_BAR_POS)? .progress_chars(PROGRESS_CHARS), ); for project in projectinfo { add_project(config, &project_p, &project, &list)?; if project.current_version.is_some() { - downloadstack.push( - project - .current_version - .ok_or(MLErr::new(EType::Other, "cur_ver"))?, - ); + downloadstack + .push(project.current_version.ok_or(Error::NoCurrentVersion)?); }; } @@ -125,7 +121,7 @@ fn add_project( project .current_version .clone() - .ok_or(MLErr::new(EType::Other, "cur_ver"))? + .ok_or(Error::NoCurrentVersion)? .id }; @@ -142,9 +138,9 @@ fn add_project( let expected_err = format!("SQL: UNIQUE constraint failed: {}.mod_id", list.id); if e.to_string() == expected_err { - Err(MLErr::new(EType::ModError, "MOD_ALREADY_ON_SELECTED_LIST")) + Err(Error::ModAlreadyOnList) } else { - Err(e) + Err(e)? } } Ok(..) => Ok(..), @@ -212,7 +208,7 @@ async fn get_mod_infos( download_link: file, set_version: *setmap .get(&project.id) - .ok_or(MLErr::new(EType::Other, "not in setmap"))?, + .ok_or(Error::VersionSetNotSet)?, }); } else { let current_id = @@ -312,10 +308,7 @@ async fn get_ver_info( /// # Errors pub fn mod_remove(config: &Cfg, id: &str, list: &List) -> MLE<()> { let progress = ProgressBar::new_spinner(); - progress.set_style( - ProgressStyle::with_template(STYLE_OPERATION) - .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?, - ); + progress.set_style(ProgressStyle::with_template(STYLE_OPERATION)?); let mod_id = mods_get_id(&config.data, id)?; @@ -351,7 +344,7 @@ pub fn mod_remove(config: &Cfg, id: &str, list: &List) -> MLE<()> { if err.to_string() == "Database: NO_MODS_USERLIST" { return Ok(()); }; - return Err(err); + return Err(err)?; } }; if mods.contains(&mod_id) { diff --git a/src/commands/update.rs b/src/commands/update.rs index 721ced5..92ea9d6 100644 --- a/src/commands/update.rs +++ b/src/commands/update.rs @@ -1,13 +1,19 @@ use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use crate::{ - apis::modrinth::{extract_current_version, versions, Version}, config::Cfg, data::list::List, db::{ + apis::modrinth::{extract_current_version, versions, Version}, + config::Cfg, + data::list::List, + db::{ mods_get_info, userlist_change_versions, userlist_get_all_ids, userlist_get_applicable_versions, userlist_get_current_version, userlist_get_set_version, - }, error::{EType, MLErr, MLE}, files::{ + }, + errors::{Error, MLE}, + files::{ clean_list_dir, delete_version, disable_version, download_versions, - }, PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION + }, + PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION, }; /// # Errors @@ -20,15 +26,9 @@ pub async fn update( ) -> MLE<()> { let mp = MultiProgress::new(); - let update_p = mp.add(ProgressBar::new( - liststack - .len() - .try_into() - .map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?, - )); + let update_p = mp.add(ProgressBar::new(liststack.len().try_into()?)); update_p.set_style( - ProgressStyle::with_template(STYLE_BAR_POS) - .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))? + ProgressStyle::with_template(STYLE_BAR_POS)? .progress_chars(PROGRESS_CHARS), ); @@ -36,26 +36,15 @@ pub async fn update( update_p.set_message(format!("Update {}", current_list.id)); let list_p = mp.insert_before(&update_p, ProgressBar::new(2)); - list_p.set_style( - ProgressStyle::with_template(STYLE_OPERATION).map_err(|_| { - MLErr::new(EType::LibIndicatif, "template error") - })?, - ); + list_p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?); list_p.set_message("Update mods"); let mods = userlist_get_all_ids(config, ¤t_list.id)?; - let list_u_p = mp.insert_before( - &list_p, - ProgressBar::new( - mods.len() - .try_into() - .map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?, - ), - ); + let list_u_p = + mp.insert_before(&list_p, ProgressBar::new(mods.len().try_into()?)); list_u_p.set_style( - ProgressStyle::with_template(STYLE_BAR_POS) - .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))? + ProgressStyle::with_template(STYLE_BAR_POS)? .progress_chars(PROGRESS_CHARS), ); @@ -63,7 +52,16 @@ pub async fn update( let mut updatestack: Vec = vec![]; for id in mods { - update_mod(config, id, list_u_p.clone(), ¤t_list, &mut updatestack, &mut current_versions, clean).await?; + update_mod( + config, + id, + list_u_p.clone(), + ¤t_list, + &mut updatestack, + &mut current_versions, + clean, + ) + .await?; } list_u_p.finish_with_message(format!( @@ -91,12 +89,11 @@ pub async fn update( let d_p = mp.insert_before( &list_p, ProgressBar::new( - current_versions.len().try_into().map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?, + current_versions.len().try_into()?, ), ); d_p.set_style( - ProgressStyle::with_template(STYLE_BAR_POS) - .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))? + ProgressStyle::with_template(STYLE_BAR_POS)? .progress_chars(PROGRESS_CHARS), ); for ver in current_versions { @@ -129,45 +126,40 @@ pub async fn update( Ok(()) } -async fn update_mod(config: &Cfg, id: String, list_u_p: ProgressBar, current_list: &List, updatestack: &mut Vec, current_versions: &mut Vec<(String, String)>, clean: bool) -> MLE<()> { - let info = mods_get_info(config, &id)?; - list_u_p.set_message(format!("Update {}", info.title)); +async fn update_mod( + config: &Cfg, + id: String, + list_u_p: ProgressBar, + current_list: &List, + updatestack: &mut Vec, + current_versions: &mut Vec<(String, String)>, + clean: bool, +) -> MLE<()> { + let info = mods_get_info(config, &id)?; + list_u_p.set_message(format!("Update {}", info.title)); - //Skip check if version is set - if userlist_get_set_version(config, ¤t_list.id, &id)? { - list_u_p.inc(1); - return Ok(()); - } + //Skip check if version is set + if userlist_get_set_version(config, ¤t_list.id, &id)? { + list_u_p.inc(1); + return Ok(()); + } - //Getting current installed version for disable or delete - let disable_version = - userlist_get_current_version(config, ¤t_list.id, &id)?; - - updatestack.push( - match specific_update( - config, - clean, - current_list.clone(), - &id, - &list_u_p, - ) - .await - { - Ok(ver) => { - current_versions.push((disable_version, id.to_string())); - ver - } - Err(e) => { - if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" { - } else { - return Err(e); - }; - list_u_p.inc(1); - return Ok(()); - } - }, - ); - list_u_p.inc(1); + //Getting current installed version for disable or delete + let disable_version = + userlist_get_current_version(config, ¤t_list.id, &id)?; + + let version = specific_update( + config, + clean, + current_list.clone(), + &id, + &list_u_p, + ).await?; + if let Some(v) = version { + updatestack.push(v); + current_versions.push((disable_version, id.to_string())); + } + list_u_p.inc(1); Ok(()) } @@ -178,7 +170,7 @@ async fn specific_update( list: List, id: &str, progress: &ProgressBar, -) -> MLE { +) -> MLE> { let applicable_versions = versions(&config.apis.modrinth, String::from(id), list.clone()).await?; @@ -192,14 +184,12 @@ async fn specific_update( } } - let mut current: Vec = vec![]; - if clean - || (versions.join("|") - != userlist_get_applicable_versions( + let mut current: Option = None; + if clean || (versions.join("|") != userlist_get_applicable_versions( config, &list.id, String::from(id), - )?) + )?) { let current_str = extract_current_version(applicable_versions.clone())?; @@ -211,15 +201,10 @@ async fn specific_update( } //get new versions - let current_ver = match applicable_versions - .into_iter() - .find(|ver| ver.id == current_str) - .ok_or("!no current version in applicable_versions") - { - Ok(v) => Ok(v), - Err(e) => Err(MLErr::new(EType::Other, e)), - }?; - current.push(current_ver.clone()); + let Some(current_ver) = applicable_versions.into_iter().find(|ver| ver.id == current_str) else { + return Err(Error::NoCurrentVersion); + }; + current = Some(current_ver.clone()); let files = ¤t_ver.files; @@ -237,11 +222,12 @@ async fn specific_update( link, id.to_string(), )?; - } - - if current.is_empty() { - return Err(MLErr::new(EType::ModError, "NO_UPDATE_AVAILABLE")); }; - Ok(current[0].clone()) + if current.is_none() { + // No Update Available + Ok(None) + } else { + Ok(current) + } } diff --git a/src/config.rs b/src/config.rs index 8312d15..f361deb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,7 +7,12 @@ use std::{ use serde::{Deserialize, Serialize}; use crate::{ - data::{gameversion::{check_game_versions, VersionLevel}, modloader::Modloader}, db::setup, error::{EType, MLErr, MLE} + data::{ + gameversion::{check_game_versions, VersionLevel}, + modloader::Modloader, + }, + db::setup, + errors::{Error, MLE}, }; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -55,14 +60,17 @@ pub struct Defaults { impl Cfg { /// # Errors pub async fn init(path: Option) -> MLE { - let configfile = match path.clone() { - Some(p) => p, - None => dirs::config_dir() - .ok_or(MLErr::new(EType::Other, "config_dir"))? + let configfile = if let Some(cf) = path.clone() { + cf + } else { + let Some(config_dir) = dirs::config_dir() else { + return Err(Error::SysDirNotFound("config".to_string())); + }; + config_dir .join("modlist") .join("config.toml") .to_string_lossy() - .to_string(), + .to_string() }; if let Some(err) = File::open(&configfile).err() { @@ -78,10 +86,8 @@ impl Cfg { .add_source( config::Environment::with_prefix("MODLIST").separator("_"), ) - .build() - .unwrap() - .try_deserialize() - .unwrap(); + .build()? + .try_deserialize()?; //Check cache if !Path::new(&config.cache).exists() { diff --git a/src/data.rs b/src/data.rs index cff0f47..f72fbcf 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,7 +1,7 @@ pub mod list; pub mod gameversion; pub mod modloader; -pub mod project; +pub mod projectinfo; pub mod modification; pub static STYLE_BAR_BYTE: &str = diff --git a/src/data/gameversion.rs b/src/data/gameversion.rs index 3868502..1bda755 100644 --- a/src/data/gameversion.rs +++ b/src/data/gameversion.rs @@ -5,11 +5,15 @@ use std::{ }; use apis::modrinth::{get_game_versions, GameVersion, GameVersionType}; -use error::{EType, MLErr, MLE}; +use errors::MLE; use indicatif::{ProgressBar, ProgressStyle}; use serde::{Deserialize, Serialize}; -use crate::{apis, error, STYLE_MESSAGE}; +use crate::{ + apis, + errors::{self, Error}, + STYLE_MESSAGE, +}; #[derive(Debug, Clone, Deserialize, Serialize)] pub enum VersionLevel { @@ -25,15 +29,11 @@ pub enum VersionLevel { /// # Errors pub async fn check_game_versions(path: &str, force: bool) -> MLE<()> { let p = ProgressBar::new(1); - p.set_style(ProgressStyle::with_template(STYLE_MESSAGE).map_err(|_| { - MLErr::new(EType::LibIndicatif, "template error") - })?); + p.set_style(ProgressStyle::with_template(STYLE_MESSAGE)?); p.set_message("Update minecraft versions"); let creation_time = fs::metadata(path)?.created()?; - if !force - && creation_time.elapsed().map_err(|_| MLErr::new(EType::LibIndicatif, "SystemTimeError"))? < Duration::from_secs(60 * 60 * 24) - { + if !force && creation_time.elapsed()? < Duration::from_secs(60 * 60 * 24) { return Ok(()); } @@ -57,7 +57,7 @@ pub fn load_game_versions(path: &str) -> MLE> { } impl VersionLevel { - pub fn from(str: &str) -> Self { + #[must_use] pub fn from(str: &str) -> Self { match str { "release" => VersionLevel::Release, "snapshot" => VersionLevel::Snapshot, @@ -85,10 +85,7 @@ impl VersionLevel { { Ok(release.version) } else { - Err(MLErr::new( - EType::Other, - "no minecraft release version found", - )) + Err(Error::MinecraftVersionNotFound) } } VersionLevel::Snapshot => { @@ -97,20 +94,14 @@ impl VersionLevel { { Ok(snapshot.version) } else { - Err(MLErr::new( - EType::Other, - "no minecraft snapshot version found", - )) + Err(Error::MinecraftVersionNotFound) } } VersionLevel::Version(v) => { if versions.any(|ver| ver.version == v) { Ok(v) } else { - Err(MLErr::new( - EType::ConfigError, - "unknown minecraft version", - )) + Err(Error::MinecraftVersionNotFound) } } } diff --git a/src/data/list.rs b/src/data/list.rs index 0045b7a..b886af1 100644 --- a/src/data/list.rs +++ b/src/data/list.rs @@ -1,4 +1,4 @@ -use crate::{config::Cfg, db::{config_get_current_list, lists_get}, error::MLE}; +use crate::{config::Cfg, db::{config_get_current_list, lists_get}, errors::MLE}; use super::modloader::Modloader; @@ -14,6 +14,6 @@ impl List { /// # Errors pub fn get_current_list(config: &Cfg) -> MLE { let id = config_get_current_list(config)?; - lists_get(config, &id) + Ok(lists_get(config, &id)?) } } diff --git a/src/data/modloader.rs b/src/data/modloader.rs index 050213f..ef2611b 100644 --- a/src/data/modloader.rs +++ b/src/data/modloader.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::error::{EType, MLErr, MLE}; +use crate::errors::ConversionError; #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub enum Modloader { @@ -14,16 +14,14 @@ pub enum Modloader { Quilt, } -impl Modloader { - /// # Errors - pub fn from(string: &str) -> MLE { - match string { +impl TryFrom<&str> for Modloader { + type Error = ConversionError; + fn try_from(value: &str) -> Result { + match value { "forge" => Ok(Modloader::Forge), "fabric" => Ok(Modloader::Fabric), "quilt" => Ok(Modloader::Quilt), - _ => { - Err(MLErr::new(EType::ArgumentError, "UNKNOWN_MODLOADER")) - } + _ => Err(ConversionError::Modloader(value.to_string())) } } } diff --git a/src/data/project.rs b/src/data/project.rs deleted file mode 100644 index 9807867..0000000 --- a/src/data/project.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::apis::modrinth::Version; - -#[derive(Debug, Clone)] -pub struct ProjectInfo { - pub mod_id: String, - pub slug: String, - pub title: String, - pub current_version: Option, - pub applicable_versions: Vec, - pub download_link: String, - pub set_version: bool, -} diff --git a/src/data/projectinfo.rs b/src/data/projectinfo.rs new file mode 100644 index 0000000..9807867 --- /dev/null +++ b/src/data/projectinfo.rs @@ -0,0 +1,12 @@ +use crate::apis::modrinth::Version; + +#[derive(Debug, Clone)] +pub struct ProjectInfo { + pub mod_id: String, + pub slug: String, + pub title: String, + pub current_version: Option, + pub applicable_versions: Vec, + pub download_link: String, + pub set_version: bool, +} diff --git a/src/db.rs b/src/db.rs index 168cbbe..003c98e 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,16 +1,39 @@ -use std::io::{Error, ErrorKind}; - use rusqlite::Connection; use crate::{ config::Cfg, data::{list::List, modloader::Modloader}, - error::{EType, MLErr, MLE}, }; +type DbE = Result; + +#[derive(Debug, thiserror::Error)] +pub enum DBError { + #[error("sqlite: {source}")] + Sqlite { + #[from] + source: rusqlite::Error, + }, + + #[error("conversion: {source}")] + Conversion { + #[from] + source: crate::errors::ConversionError, + }, + + #[error("not enough arguments")] + NotEnoughArguments, + + #[error("couldn't find requested mod/version")] + NotFound, + + #[error("no mods/versions are available in the requested scope")] + NotAvailable, +} + //MODS /// # Errors -pub fn mods_insert(config: &Cfg, id: &str, slug: &str, name: &str) -> MLE<()> { +pub fn mods_insert(config: &Cfg, id: &str, slug: &str, name: &str) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -23,9 +46,7 @@ pub fn mods_insert(config: &Cfg, id: &str, slug: &str, name: &str) -> MLE<()> { } /// # Errors -pub fn mods_get_all_ids( - config: &Cfg, -) -> Result, Box> { +pub fn mods_get_all_ids(config: &Cfg) -> DbE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -39,7 +60,7 @@ pub fn mods_get_all_ids( } if mods.is_empty() { - Err(Box::new(Error::new(ErrorKind::NotFound, "NO_MODS_ALL"))) + Err(DBError::NotFound) } else { Ok(mods) } @@ -53,8 +74,8 @@ pub fn mods_get_all_ids( /// /// # Errors /// -/// Will return `MLError` when no mod id is found -pub fn mods_get_id(data: &str, slug: &str) -> MLE { +/// Will return `DBError` when no mod id is found +pub fn mods_get_id(data: &str, slug: &str) -> DbE { let data = format!("{data}/data.db"); let connection = Connection::open(data)?; @@ -91,7 +112,7 @@ pub fn mods_get_id(data: &str, slug: &str) -> MLE { } if mod_id.is_empty() { - return Err(MLErr::new(EType::DBError, "GI_MOD_NOT_FOUND")); + Err(DBError::NotFound)?; }; Ok(mod_id) @@ -103,7 +124,7 @@ pub struct ModInfo { } /// # Errors -pub fn mods_get_info(config: &Cfg, id: &str) -> MLE { +pub fn mods_get_info(config: &Cfg, id: &str) -> DbE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -125,15 +146,15 @@ pub fn mods_get_info(config: &Cfg, id: &str) -> MLE { }); } - if mod_info.is_none() { - Err(MLErr::new(EType::DBError, "GN_MOD_NOT_FOUND")) + if let Some(mi) = mod_info { + Ok(mi) } else { - Ok(mod_info.ok_or(MLErr::new(EType::Other, "mod_info"))?) + Err(DBError::NotFound)? } } /// # Errors -pub fn mods_remove(config: &Cfg, id: &str) -> MLE<()> { +pub fn mods_remove(config: &Cfg, id: &str) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -152,12 +173,12 @@ pub struct DBModlistVersions { pub fn mods_get_versions( config: &Cfg, mods: &[String], -) -> MLE> { +) -> DbE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; if mods.is_empty() { - return Err(MLErr::new(EType::ArgumentError, "MODS_NO_INPUT")); + Err(DBError::NotEnoughArguments)?; } let mut wherestr = String::from("WHERE"); @@ -190,7 +211,7 @@ pub fn mods_get_versions( } if versionmaps.is_empty() { - Err(MLErr::new(EType::DBError, "MODS_MODS_NOT_FOUND")) + Err(DBError::NotFound) } else { Ok(versionmaps) } @@ -206,7 +227,7 @@ pub fn userlist_insert( applicable_versions: &[String], current_link: &str, set_version: bool, -) -> MLE<()> { +) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -228,7 +249,7 @@ pub fn userlist_insert( } /// # Errors -pub fn userlist_get_all_ids(config: &Cfg, list_id: &str) -> MLE> { +pub fn userlist_get_all_ids(config: &Cfg, list_id: &str) -> DbE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -242,17 +263,14 @@ pub fn userlist_get_all_ids(config: &Cfg, list_id: &str) -> MLE> { } if mod_ids.is_empty() { - Err(MLErr::new( - EType::DBError, - &format!("NO_MODS_USERLIST{list_id}"), - )) + Err(DBError::NotAvailable) } else { Ok(mod_ids) } } /// # Errors -pub fn userlist_remove(config: &Cfg, list_id: &str, mod_id: &str) -> MLE<()> { +pub fn userlist_remove(config: &Cfg, list_id: &str, mod_id: &str) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -268,7 +286,7 @@ pub fn userlist_get_applicable_versions( config: &Cfg, list_id: &str, mod_id: String, -) -> MLE { +) -> DbE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -285,7 +303,7 @@ pub fn userlist_get_applicable_versions( } if version.is_empty() { - Err(MLErr::new(EType::DBError, "GAV_MOD_NOT_FOUND")) + Err(DBError::NotFound) } else { Ok(version) } @@ -295,7 +313,7 @@ pub fn userlist_get_applicable_versions( pub fn userlist_get_all_applicable_versions_with_mods( config: &Cfg, list_id: &str, -) -> MLE> { +) -> DbE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -316,7 +334,7 @@ pub fn userlist_get_all_applicable_versions_with_mods( } if versions.is_empty() { - return Err(MLErr::new(EType::DBError, "NO_MODS_ON_LIST")); + return Err(DBError::NotAvailable); }; Ok(versions) @@ -327,7 +345,7 @@ pub fn userlist_get_current_version( config: &Cfg, list_id: &str, mod_id: &str, -) -> MLE { +) -> DbE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -344,7 +362,7 @@ pub fn userlist_get_current_version( } if version.is_empty() { - Err(MLErr::new(EType::DBError, "GCV_MOD_NOT_FOUND")) + Err(DBError::NotFound) } else { Ok(version) } @@ -354,7 +372,7 @@ pub fn userlist_get_current_version( pub fn userlist_get_all_current_version_ids( config: &Cfg, list_id: &str, -) -> MLE> { +) -> DbE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -368,7 +386,7 @@ pub fn userlist_get_all_current_version_ids( } if versions.is_empty() { - return Err(MLErr::new(EType::DBError, "NO_MODS_ON_LIST")); + return Err(DBError::NotAvailable); }; Ok(versions) @@ -378,7 +396,7 @@ pub fn userlist_get_all_current_version_ids( pub fn userlist_get_all_current_versions_with_mods( config: &Cfg, list_id: &str, -) -> Result, Box> { +) -> DbE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -399,10 +417,7 @@ pub fn userlist_get_all_current_versions_with_mods( } if versions.is_empty() { - return Err(Box::new(std::io::Error::new( - ErrorKind::Other, - "NO_MODS_ON_LIST", - ))); + return Err(DBError::NotAvailable); }; Ok(versions) @@ -413,7 +428,7 @@ pub fn userlist_get_set_version( config: &Cfg, list_id: &str, mod_id: &str, -) -> MLE { +) -> DbE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -439,7 +454,7 @@ pub fn userlist_change_versions( versions: String, link: String, mod_id: String, -) -> MLE<()> { +) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -453,7 +468,7 @@ pub fn userlist_add_disabled_versions( list_id: &str, disabled_version: String, mod_id: String, -) -> MLE<()> { +) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -480,7 +495,7 @@ pub fn userlist_get_disabled_versions( config: &Cfg, list_id: &str, mod_id: String, -) -> MLE { +) -> DbE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -497,7 +512,7 @@ pub fn userlist_get_disabled_versions( } if version.is_empty() { - Err(MLErr::new(EType::DBError, "GDV_MOD_NOT_FOUND")) + Err(DBError::NotFound) } else { Ok(version) } @@ -507,7 +522,7 @@ pub fn userlist_get_disabled_versions( pub fn userlist_get_all_downloads( config: &Cfg, list_id: &str, -) -> Result, Box> { +) -> DbE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -522,10 +537,7 @@ pub fn userlist_get_all_downloads( } if links.is_empty() { - return Err(Box::new(std::io::Error::new( - ErrorKind::Other, - "NO_MODS_ON_LIST", - ))); + return Err(DBError::NotAvailable); }; Ok(links) @@ -540,7 +552,7 @@ pub fn lists_insert( mc_version: &str, mod_loader: &Modloader, download_folder: &str, -) -> MLE<()> { +) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -554,7 +566,7 @@ pub fn lists_insert( } /// # Errors -pub fn lists_remove(config: &Cfg, id: &str) -> MLE<()> { +pub fn lists_remove(config: &Cfg, id: &str) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -564,7 +576,7 @@ pub fn lists_remove(config: &Cfg, id: &str) -> MLE<()> { } /// # Errors -pub fn lists_get(config: &Cfg, list_id: &str) -> MLE { +pub fn lists_get(config: &Cfg, list_id: &str) -> DbE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -591,20 +603,20 @@ pub fn lists_get(config: &Cfg, list_id: &str) -> MLE { list = List { id: list_id.to_string(), mc_version: String::from(&li[0]), - modloader: Modloader::from(&li[1])?, + modloader: Modloader::try_from(li[1].as_str())?, download_folder: String::from(&li[2]), }; } if list.id.is_empty() { - return Err(MLErr::new(EType::DBError, "LIST_NOT_FOUND")); + Err(DBError::NotFound)?; } Ok(list) } /// # Errors -pub fn lists_version(config: &Cfg, list_id: &str, version: &str) -> MLE<()> { +pub fn lists_version(config: &Cfg, list_id: &str, version: &str) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -616,7 +628,7 @@ pub fn lists_version(config: &Cfg, list_id: &str, version: &str) -> MLE<()> { } /// # Errors -pub fn lists_get_all_ids(config: &Cfg) -> MLE> { +pub fn lists_get_all_ids(config: &Cfg) -> DbE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -629,7 +641,7 @@ pub fn lists_get_all_ids(config: &Cfg) -> MLE> { } if list_ids.is_empty() { - Err(MLErr::new(EType::DBError, "NO_LISTS")) + Err(DBError::NotAvailable) } else { Ok(list_ids) } @@ -637,7 +649,7 @@ pub fn lists_get_all_ids(config: &Cfg) -> MLE> { //config /// # Errors -pub fn config_change_current_list(config: &Cfg, id: &str) -> MLE<()> { +pub fn config_change_current_list(config: &Cfg, id: &str) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -649,7 +661,7 @@ pub fn config_change_current_list(config: &Cfg, id: &str) -> MLE<()> { } /// # Errors -pub fn config_get_current_list(config: &Cfg) -> MLE { +pub fn config_get_current_list(config: &Cfg) -> DbE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -663,7 +675,7 @@ pub fn config_get_current_list(config: &Cfg) -> MLE { } if list_id.is_empty() { - return Err(MLErr::new(EType::DBError, "NO_CURRENT_LIST")); + return Err(DBError::NotAvailable); } Ok(list_id) @@ -720,7 +732,7 @@ pub fn s_config_update_version( /// # Errors pub fn s_config_get_version( config: &Cfg, -) -> Result> { +) -> DbE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; @@ -734,10 +746,7 @@ pub fn s_config_get_version( } if version.is_empty() { - return Err(Box::new(std::io::Error::new( - ErrorKind::Other, - "NO_DBVERSION", - ))); + return Err(DBError::NotAvailable); }; Ok(version) } @@ -749,18 +758,14 @@ pub fn s_insert_column( column: &str, c_type: &str, default: Option, -) -> Result<(), Box> { +) -> DbE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut sql = format!("ALTER TABLE {table} ADD '{column}' {c_type}"); - if default.is_some() { - sql = format!( - "{} DEFAULT {}", - sql, - default.ok_or(MLErr::new(EType::Other, "errornous default"))? - ); + if let Some(def) = default { + sql = format!("{sql} DEFAULT {def}"); } connection.execute(sql.as_str(), ())?; @@ -768,7 +773,7 @@ pub fn s_insert_column( } /// # Errors -pub fn setup(path: &str) -> MLE<()> { +pub fn setup(path: &str) -> DbE<()> { let connection = Connection::open(path)?; connection.execute_batch( diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 652fa0c..0000000 --- a/src/error.rs +++ /dev/null @@ -1,129 +0,0 @@ -use core::fmt; -use serde::Deserialize; - -pub type MLE = Result; - -#[derive(Debug, Deserialize)] -pub struct MLErr { - etype: EType, - message: String, -} - -#[derive(Debug, Deserialize)] -pub enum EType { - ArgumentError, - ArgumentCountError, - ConfigError, - DBError, - ModError, - LibToml, - LibSql, - LibReq, - LibChrono, - LibJson, - LibIndicatif, - IoError, - Other, -} - -impl std::error::Error for MLErr { - fn description(&self) -> &str { - &self.message - } -} - -impl fmt::Display for MLErr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.etype { - EType::ArgumentError => { - write!(f, "User input not accepted: {}", self.message) - } - EType::ArgumentCountError => { - write!(f, "Too many/too few arguments") - } - EType::ConfigError => write!(f, "CONFIG"), - EType::DBError => write!(f, "Database: {}", self.message), - EType::ModError => write!(f, "Mod: {}", self.message), - EType::LibToml => write!(f, "TOML"), - EType::LibSql => write!(f, "SQL: {}", self.message), - EType::LibReq => write!(f, "REQWEST"), - EType::LibChrono => write!(f, "Chrono error: {}", self.message), - EType::LibJson => write!(f, "JSON: {}", self.message), - EType::LibIndicatif => write!(f, "Indicativ: {}", self.message), - EType::IoError => write!(f, "IO"), - EType::Other => write!(f, "OTHER"), - } - } -} - -impl From for MLErr { - fn from(error: reqwest::Error) -> Self { - Self { - etype: EType::LibReq, - message: error.to_string(), - } - } -} - -impl From for MLErr { - fn from(error: toml::de::Error) -> Self { - Self { - etype: EType::LibToml, - message: error.to_string(), - } - } -} - -impl From for MLErr { - fn from(error: rusqlite::Error) -> Self { - Self { - etype: EType::LibSql, - message: error.to_string(), - } - } -} - -impl From for MLErr { - fn from(error: toml::ser::Error) -> Self { - Self { - etype: EType::LibToml, - message: error.to_string(), - } - } -} - -impl From for MLErr { - fn from(error: chrono::ParseError) -> Self { - Self { - etype: EType::LibChrono, - message: error.to_string(), - } - } -} - -impl From for MLErr { - fn from(error: std::io::Error) -> Self { - Self { - etype: EType::IoError, - message: error.to_string(), - } - } -} - -impl From for MLErr { - fn from(value: serde_json::error::Error) -> Self { - Self { - etype: EType::LibJson, - message: value.to_string(), - } - } -} - -impl MLErr { - #[must_use] pub fn new(etype: EType, message: &str) -> Self { - Self { - etype, - message: String::from(message), - } - } -} diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..be2ca3c --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,132 @@ +use std::{io, num::TryFromIntError, time::SystemTimeError}; + +use chrono::ParseError; +use indicatif::style::TemplateError; +use reqwest::StatusCode; + +use crate::db::DBError; + +pub type MLE = Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("io: {source}")] + Io { + #[from] + source: io::Error, + }, + + #[error("tryfromint: {source}")] + TryFromInt { + #[from] + source: TryFromIntError, + }, + + #[error("serde_json: {source}")] + Json { + #[from] + source: serde_json::Error, + }, + + #[error("reqwest: {source}")] + Reqwest { + #[from] + source: reqwest::Error, + }, + + #[error("chrono parse error: {source}")] + ChronoParse { + #[from] + source: ParseError, + }, + + #[error("config: {source}")] + Config { + #[from] + source: config::ConfigError, + }, + + #[error("indicatif template: {source}")] + IndicatifTemplate { + #[from] + source: TemplateError, + }, + + #[error("toml: {source}")] + TomlSer { + #[from] + source: toml::ser::Error, + }, + + #[error("toml: {source}")] + TomlDeser { + #[from] + source: toml::de::Error, + }, + + #[error("systemtime: {source}")] + Systemtime { + #[from] + source: SystemTimeError, + }, + + #[error("conversion: {source}")] + Conversion { + #[from] + source: ConversionError, + }, + + #[error("Request didn't return 200 OK ({0})")] + RequestNotOK(StatusCode), + + #[error("ModError ({0})")] + ModError(String), + + #[error("No file extension where one should be")] + NoFileExtension, + + #[error("Desired path not found")] + PathNotFound, + + #[error("Desired system directory {0} not found")] + SysDirNotFound(String), + + #[error("No requested Minecraft version found")] + MinecraftVersionNotFound, + + #[error("Mod already added")] + ModAlreadyOnList, + + #[error("No versions are applicable for the current scope")] + NoCurrentVersion, + + #[error("The version does not know if it's locked or not")] + VersionSetNotSet, + + #[error("No download for the current id found")] + NoDownload, + + #[error("No ProjectInfo found / no id given")] + NoProjectInfo, + + #[error("The requested List was not found")] + ListNotFound, + + #[error("No download folder given")] + NoDownloadFolder, + + #[error("db: {source}")] + DB { + #[from] + source: DBError, + }, +} + +#[derive(Debug, thiserror::Error)] +pub enum ConversionError { + #[error("couldn't convert to Modloader from: {0}")] + Modloader(String), + + #[error("Path couldn't be converted to string")] + InvalidPath, +} diff --git a/src/files.rs b/src/files.rs index 98785fd..2a6e949 100644 --- a/src/files.rs +++ b/src/files.rs @@ -10,7 +10,13 @@ use std::{ use tokio::task::JoinSet; use crate::{ - apis::modrinth::Version, cache::{copy_cached_version, get_cached_versions}, config::Cfg, data::list::List, db::{mods_get_info, userlist_add_disabled_versions}, error::{EType, MLErr, MLE}, PROGRESS_CHARS, STYLE_BAR_BYTE, STYLE_BAR_POS, STYLE_SPINNER + apis::modrinth::Version, + cache::{copy_cached_version, get_cached_versions}, + config::Cfg, + data::list::List, + db::{mods_get_info, userlist_add_disabled_versions}, + errors::{ConversionError, Error, MLE}, + PROGRESS_CHARS, STYLE_BAR_BYTE, STYLE_BAR_POS, STYLE_SPINNER, }; /// # Errors @@ -25,15 +31,14 @@ pub async fn download_versions( let mut js = JoinSet::new(); - let style_spinner = ProgressStyle::with_template(STYLE_SPINNER).map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?; + let style_spinner = ProgressStyle::with_template(STYLE_SPINNER)?; let all = progress.insert_before( progress_before, - ProgressBar::new(versions.len().try_into().map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?), + ProgressBar::new(versions.len().try_into()?), ); all.set_style( - ProgressStyle::with_template(STYLE_BAR_POS) - .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))? + ProgressStyle::with_template(STYLE_BAR_POS)? .progress_chars(PROGRESS_CHARS), ); all.set_message(format!("âś“Downloading {}", list.id)); @@ -87,8 +92,8 @@ async fn download_version( None => files[0].clone(), }; let mut splitname: Vec<&str> = file.filename.split('.').collect(); - let Ok(extension) = splitname.pop().ok_or("") else { - return Err(MLErr::new(EType::Other, "NO_FILE_EXTENSION")) + let Some(extension) = splitname.pop() else { + return Err(Error::NoFileExtension); }; let filename = format!( "{}.mr.{}.{}.{}", @@ -169,7 +174,12 @@ pub fn disable_version( rename(file, disabled)?; - userlist_add_disabled_versions(config, ¤t_list.id, versionid, mod_id)?; + userlist_add_disabled_versions( + config, + ¤t_list.id, + versionid, + mod_id, + )?; Ok(()) } @@ -189,8 +199,8 @@ pub fn get_file_path(list: &List, versionid: &str) -> MLE { for file in read_dir(&list.download_folder)? { let path = file?.path(); if path.is_file() { - let Ok(pathstr) = path.to_str().ok_or("") else { - return Err(MLErr::new(EType::Other, "INVALID_PATH")) + let Some(pathstr) = path.to_str() else { + return Err(Error::PathNotFound); }; let namesplit: Vec<&str> = pathstr.split('.').collect(); let ver_id = namesplit[namesplit.len() - 2]; @@ -198,11 +208,8 @@ pub fn get_file_path(list: &List, versionid: &str) -> MLE { } } - let Ok(filename) = names.get(versionid).ok_or("") else { - return Err(MLErr::new( - EType::ArgumentError, - "VERSION_NOT_FOUND_IN_FILES", - )) + let Some(filename) = names.get(versionid) else { + return Err(Error::PathNotFound); }; Ok(filename.to_owned()) @@ -213,13 +220,16 @@ pub fn get_downloaded_versions(list: &List) -> MLE> { let mut versions: HashMap = HashMap::new(); for file in read_dir(&list.download_folder)? { let path = file?.path(); - if path.is_file() - && path - .extension() - .ok_or(MLErr::new(EType::IoError, "extension"))? - == "jar" - { - let pathstr = path.to_str().ok_or(MLErr::new(EType::IoError, "path_to_str"))?; + if path.is_file() { + let Some(extension) = path.extension() else { + return Err(Error::NoFileExtension); + }; + if extension != "jar" { + continue; + } + let Some(pathstr) = path.to_str() else { + return Err(ConversionError::InvalidPath)?; + }; let namesplit: Vec<&str> = pathstr.split('.').collect(); versions.insert( String::from(namesplit[namesplit.len() - 3]), diff --git a/src/main.rs b/src/main.rs index 038e2f4..1b8f041 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ pub mod cache; pub mod commands; pub mod config; pub mod db; -pub mod error; +pub mod errors; pub mod files; pub mod data; @@ -14,7 +14,7 @@ use config::Cfg; use data::{gameversion::VersionLevel, list::List, modification::{AddMod, IDSelector}, modloader::Modloader}; pub use data::{STYLE_BAR_POS, STYLE_MESSAGE, STYLE_SPINNER, STYLE_BAR_BYTE, STYLE_OPERATION, PROGRESS_CHARS}; use db::{config_get_current_list, lists_get, lists_get_all_ids}; -use error::MLE; +use errors::MLE; #[derive(Parser)] #[command(author, version, about)] @@ -297,7 +297,7 @@ async fn handle_list( version, } => { let ml = match modloader { - Some(ml) => Modloader::from(&ml).unwrap(), + Some(ml) => Modloader::try_from(ml.as_str())?, None => config.defaults.modloader.clone(), }; -- cgit v1.2.3