summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/apis/modrinth.rs51
-rw-r--r--src/cache.rs12
-rw-r--r--src/commands/download.rs44
-rw-r--r--src/commands/io.rs29
-rw-r--r--src/commands/list.rs38
-rw-r--r--src/commands/modification.rs63
-rw-r--r--src/commands/update.rs160
-rw-r--r--src/config.rs26
-rw-r--r--src/data.rs2
-rw-r--r--src/data/gameversion.rs33
-rw-r--r--src/data/list.rs4
-rw-r--r--src/data/modloader.rs14
-rw-r--r--src/data/projectinfo.rs (renamed from src/data/project.rs)0
-rw-r--r--src/db.rs151
-rw-r--r--src/error.rs129
-rw-r--r--src/errors.rs132
-rw-r--r--src/files.rs54
-rw-r--r--src/main.rs6
18 files changed, 451 insertions, 497 deletions
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;
3use serde::{Deserialize, Serialize}; 3use serde::{Deserialize, Serialize};
4 4
5use crate::{ 5use crate::{
6 error::{EType, MLErr, MLE}, 6 errors::{Error, MLE},
7 List, 7 List,
8}; 8};
9 9
@@ -131,10 +131,7 @@ pub enum GameVersionType {
131} 131}
132 132
133/// # Errors 133/// # Errors
134async fn get( 134async fn get(api: &str, path: &str) -> Result<Vec<u8>, Error> {
135 api: &str,
136 path: &str,
137) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> {
138 let url = format!(r#"{api}{path}"#); 135 let url = format!(r#"{api}{path}"#);
139 136
140 let client = Client::builder() 137 let client = Client::builder()
@@ -145,23 +142,19 @@ async fn get(
145 .build()?; 142 .build()?;
146 let res = client.get(url).send().await?; 143 let res = client.get(url).send().await?;
147 144
148 let mut data: Option<Vec<u8>> = None; 145 if res.status() != 200 {
149 146 return Err(Error::RequestNotOK(res.status()));
150 if res.status() == 200 {
151 data = Some(res.bytes().await?.to_vec());
152 } 147 }
153 148
154 Ok(data) 149 Ok(res.bytes().await?.to_vec())
155} 150}
156 151
157/// # Errors 152/// # Errors
158pub async fn project(api: &str, name: &str) -> MLE<Project> { 153pub async fn project(api: &str, name: &str) -> MLE<Project> {
159 let url = format!("project/{name}"); 154 let url = format!("project/{name}");
160 let data = get(api, &url).await 155 let data = get(api, &url).await?;
161 .map_err(|_| MLErr::new(EType::Other, "geterr"))?
162 .ok_or(MLErr::new(EType::Other, "geterr2"))?;
163 156
164 serde_json::from_slice(&data).map_err(|_| MLErr::new(EType::LibJson, "from project")) 157 Ok(serde_json::from_slice(&data)?)
165} 158}
166 159
167/// # Errors 160/// # Errors
@@ -169,11 +162,9 @@ pub async fn projects(api: &str, ids: Vec<String>) -> MLE<Vec<Project>> {
169 let all = ids.join(r#"",""#); 162 let all = ids.join(r#"",""#);
170 let url = format!(r#"projects?ids=["{all}"]"#); 163 let url = format!(r#"projects?ids=["{all}"]"#);
171 164
172 let data = get(api, &url).await 165 let data = get(api, &url).await?;
173 .map_err(|_| MLErr::new(EType::Other, "geterr"))?
174 .ok_or(MLErr::new(EType::Other, "geterr2"))?;
175 166
176 serde_json::from_slice(&data).map_err(|_| MLErr::new(EType::LibJson, "from projects")) 167 Ok(serde_json::from_slice(&data)?)
177} 168}
178 169
179///Get applicable versions from `mod_id` with list context 170///Get applicable versions from `mod_id` with list context
@@ -184,12 +175,12 @@ pub async fn versions(api: &str, id: String, list: List) -> MLE<Vec<Version>> {
184 id, list.modloader, list.mc_version 175 id, list.modloader, list.mc_version
185 ); 176 );
186 177
187 let data = get(api, &url).await 178 let data = get(api, &url).await?;
188 .map_err(|_| MLErr::new(EType::Other, "geterr"))?;
189 179
190 Ok(match data { 180 Ok(if data.is_empty() {
191 Some(data) => serde_json::from_slice(&data).map_err(|_| MLErr::new(EType::LibJson, "from version"))?, 181 Vec::new()
192 None => Vec::new(), 182 } else {
183 serde_json::from_slice(&data)?
193 }) 184 })
194} 185}
195 186
@@ -201,17 +192,15 @@ pub async fn get_raw_versions(
201) -> MLE<Vec<Version>> { 192) -> MLE<Vec<Version>> {
202 let url = format!(r#"versions?ids=["{}"]"#, versions.join(r#"",""#)); 193 let url = format!(r#"versions?ids=["{}"]"#, versions.join(r#"",""#));
203 194
204 let data = get(api, &url).await 195 let data = get(api, &url).await?;
205 .map_err(|_| MLErr::new(EType::Other, "geterr"))?
206 .ok_or(MLErr::new(EType::Other, "geterr2"))?;
207 196
208 serde_json::from_slice(&data).map_err(|_| MLErr::new(EType::LibJson, "from raw version")) 197 Ok(serde_json::from_slice(&data)?)
209} 198}
210 199
211/// # Errors 200/// # Errors
212pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> { 201pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
213 match versions.len() { 202 match versions.len() {
214 0 => Err(MLErr::new(EType::ModError, "NO_VERSIONS_AVAILABLE")), 203 0 => Err(Error::ModError("NO_VERSIONS_AVAILABLE".to_string())),
215 1.. => { 204 1.. => {
216 let mut times: Vec<(String, DateTime<FixedOffset>)> = vec![]; 205 let mut times: Vec<(String, DateTime<FixedOffset>)> = vec![];
217 for ver in versions { 206 for ver in versions {
@@ -228,9 +217,7 @@ pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
228/// # Errors 217/// # Errors
229pub async fn get_game_versions() -> MLE<Vec<GameVersion>> { 218pub async fn get_game_versions() -> MLE<Vec<GameVersion>> {
230 let data = get("https://api.modrinth.com/v2/", "tag/game_version") 219 let data = get("https://api.modrinth.com/v2/", "tag/game_version")
231 .await 220 .await?;
232 .map_err(|_| MLErr::new(EType::Other, "geterr"))?
233 .ok_or(MLErr::new(EType::Other, "geterr2"))?;
234 221
235 serde_json::from_slice(&data).map_err(|_| MLErr::new(EType::LibJson, "from game version")) 222 Ok(serde_json::from_slice(&data)?)
236} 223}
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::{
3 fs::{copy, read_dir}, 3 fs::{copy, read_dir},
4}; 4};
5 5
6use crate::error::{EType, MLErr, MLE}; 6use crate::errors::{ConversionError, Error, MLE};
7 7
8/// # Errors 8/// # Errors
9pub fn get_cached_versions(path: &str) -> MLE<HashMap<String, String>> { 9pub fn get_cached_versions(path: &str) -> MLE<HashMap<String, String>> {
10 let mut versions: HashMap<String, String> = HashMap::new(); 10 let mut versions: HashMap<String, String> = HashMap::new();
11 for file in read_dir(path).map_err(|_| MLErr::new(EType::IoError, "readdir"))? { 11 for file in read_dir(path)? {
12 let path = file.map_err(|_| MLErr::new(EType::IoError, "file"))?.path(); 12 let path = file?.path();
13 if path.is_file() && path.extension().ok_or(MLErr::new(EType::IoError, "ext"))? == "jar" { 13 if path.is_file() && path.extension().ok_or(Error::NoFileExtension)? == "jar" {
14 let pathstr = path.to_str().ok_or(MLErr::new(EType::IoError, "path"))?; 14 let pathstr = path.to_str().ok_or(ConversionError::InvalidPath)?;
15 let namesplit: Vec<&str> = pathstr.split('.').collect(); 15 let namesplit: Vec<&str> = pathstr.split('.').collect();
16 versions.insert( 16 versions.insert(
17 String::from(namesplit[namesplit.len() - 2]), 17 String::from(namesplit[namesplit.len() - 2]),
@@ -27,6 +27,6 @@ pub fn copy_cached_version(version_path: &str, download_path: &str) -> MLE<()> {
27 let versplit: Vec<&str> = version_path.split('/').collect(); 27 let versplit: Vec<&str> = version_path.split('/').collect();
28 let download = 28 let download =
29 format!("{}/{}", download_path, versplit[versplit.len() - 1]); 29 format!("{}/{}", download_path, versplit[versplit.len() - 1]);
30 copy(version_path, download).map_err(|err| MLErr::new(EType::IoError, &err.to_string()))?; 30 copy(version_path, download)?;
31 Ok(()) 31 Ok(())
32} 32}
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 @@
1use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; 1use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
2 2
3use crate::apis::modrinth::get_raw_versions; 3use crate::apis::modrinth::get_raw_versions;
4use crate::errors::Error;
4use crate::{config::Cfg, List}; 5use crate::{config::Cfg, List};
5use crate::{ 6use crate::{
6 db::userlist_get_all_current_versions_with_mods, 7 db::userlist_get_all_current_versions_with_mods,
7 error::{EType, MLErr, MLE}, 8 errors::MLE,
8 files::{ 9 files::{
9 clean_list_dir, delete_version, disable_version, download_versions, 10 clean_list_dir, delete_version, disable_version, download_versions,
10 get_downloaded_versions, 11 get_downloaded_versions,
@@ -20,20 +21,22 @@ pub async fn download(
20 delete_old: bool, 21 delete_old: bool,
21) -> MLE<()> { 22) -> MLE<()> {
22 let mp = MultiProgress::new(); 23 let mp = MultiProgress::new();
23 let download_p = mp.add(ProgressBar::new( 24 let download_p = mp.add(ProgressBar::new(liststack.len().try_into()?));
24 liststack
25 .len()
26 .try_into()
27 .map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?,
28 ));
29 download_p.set_style( 25 download_p.set_style(
30 ProgressStyle::with_template(STYLE_BAR_POS) 26 ProgressStyle::with_template(STYLE_BAR_POS)?
31 .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
32 .progress_chars(PROGRESS_CHARS), 27 .progress_chars(PROGRESS_CHARS),
33 ); 28 );
34 29
35 for current_list in liststack { 30 for current_list in liststack {
36 download_list(config, mp.clone(), download_p.clone(), current_list, clean, delete_old).await?; 31 download_list(
32 config,
33 mp.clone(),
34 download_p.clone(),
35 current_list,
36 clean,
37 delete_old,
38 )
39 .await?;
37 } 40 }
38 41
39 download_p.finish_with_message("Downloaded all lists"); 42 download_p.finish_with_message("Downloaded all lists");
@@ -52,13 +55,8 @@ async fn download_list(
52 download_p.set_message(format!("Download in {}", current_list.id)); 55 download_p.set_message(format!("Download in {}", current_list.id));
53 56
54 let downloaded_versions = get_downloaded_versions(&current_list)?; 57 let downloaded_versions = get_downloaded_versions(&current_list)?;
55 let current_version_ids = match userlist_get_all_current_versions_with_mods( 58 let current_version_ids =
56 config, 59 userlist_get_all_current_versions_with_mods(config, &current_list.id)?;
57 &current_list.id,
58 ) {
59 Ok(i) => Ok(i),
60 Err(e) => Err(MLErr::new(EType::DBError, e.to_string().as_str())),
61 }?;
62 60
63 let mut to_download: Vec<String> = vec![]; 61 let mut to_download: Vec<String> = vec![];
64 //(mod_id, version_id) 62 //(mod_id, version_id)
@@ -74,7 +72,7 @@ async fn download_list(
74 to_download.push(current_version); 72 to_download.push(current_version);
75 } else { 73 } else {
76 let downloaded_version = 74 let downloaded_version =
77 current_download.ok_or(MLErr::new(EType::Other, "IDK, WTF"))?; 75 current_download.ok_or(Error::NoDownload)?;
78 if &current_version != downloaded_version { 76 if &current_version != downloaded_version {
79 to_disable 77 to_disable
80 .push((mod_id.clone(), String::from(downloaded_version))); 78 .push((mod_id.clone(), String::from(downloaded_version)));
@@ -106,16 +104,10 @@ async fn download_list(
106 if !to_disable.is_empty() { 104 if !to_disable.is_empty() {
107 let d_p = mp.insert_before( 105 let d_p = mp.insert_before(
108 &download_p, 106 &download_p,
109 ProgressBar::new( 107 ProgressBar::new(to_disable.len().try_into()?),
110 to_disable
111 .len()
112 .try_into()
113 .map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?,
114 ),
115 ); 108 );
116 d_p.set_style( 109 d_p.set_style(
117 ProgressStyle::with_template(STYLE_BAR_POS) 110 ProgressStyle::with_template(STYLE_BAR_POS)?
118 .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
119 .progress_chars(PROGRESS_CHARS), 111 .progress_chars(PROGRESS_CHARS),
120 ); 112 );
121 for ver in to_disable { 113 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;
4use std::io::prelude::*; 4use std::io::prelude::*;
5 5
6use crate::{ 6use crate::{
7 config::Cfg, data::modification::{AddMod, IDSelector}, db::{ 7 config::Cfg,
8 data::modification::{AddMod, IDSelector},
9 db::{
8 lists_get, lists_get_all_ids, lists_insert, userlist_get_all_ids, 10 lists_get, lists_get_all_ids, lists_insert, userlist_get_all_ids,
9 userlist_get_current_version, userlist_get_set_version, 11 userlist_get_current_version, userlist_get_set_version,
10 }, error::{EType, MLErr, MLE}, mod_add, List, Modloader, STYLE_OPERATION 12 },
13 errors::{ConversionError, Error, MLE},
14 mod_add, List, Modloader, STYLE_OPERATION,
11}; 15};
12 16
13#[derive(Debug, Serialize, Deserialize)] 17#[derive(Debug, Serialize, Deserialize)]
@@ -67,22 +71,13 @@ impl ExportList {
67/// # Errors 71/// # Errors
68pub fn export(config: &Cfg, list: Option<String>) -> MLE<()> { 72pub fn export(config: &Cfg, list: Option<String>) -> MLE<()> {
69 let progress = ProgressBar::new_spinner(); 73 let progress = ProgressBar::new_spinner();
70 progress.set_style( 74 progress.set_style(ProgressStyle::with_template(STYLE_OPERATION)?);
71 ProgressStyle::with_template(STYLE_OPERATION)
72 .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?,
73 );
74 75
75 let mut list_ids: Vec<String> = vec![]; 76 let mut list_ids: Vec<String> = vec![];
76 if list.is_none() { 77 if list.is_none() {
77 list_ids = lists_get_all_ids(config)?; 78 list_ids = lists_get_all_ids(config)?;
78 } else { 79 } else {
79 list_ids.push( 80 list_ids.push(lists_get(config, &list.ok_or(Error::ListNotFound)?)?.id);
80 lists_get(
81 config,
82 &list.ok_or(MLErr::new(EType::Other, "nolist"))?,
83 )?
84 .id,
85 );
86 } 81 }
87 82
88 let mut lists: Vec<ExportList> = vec![]; 83 let mut lists: Vec<ExportList> = vec![];
@@ -95,11 +90,11 @@ pub fn export(config: &Cfg, list: Option<String>) -> MLE<()> {
95 let toml = toml::to_string(&Export { lists })?; 90 let toml = toml::to_string(&Export { lists })?;
96 91
97 let filestr = dirs::home_dir() 92 let filestr = dirs::home_dir()
98 .ok_or(MLErr::new(EType::Other, "no home"))? 93 .ok_or(Error::SysDirNotFound("home".to_string()))?
99 .join("mlexport.toml") 94 .join("mlexport.toml")
100 .into_os_string() 95 .into_os_string()
101 .into_string() 96 .into_string()
102 .map_err(|_| MLErr::new(EType::IoError, "No String"))?; 97 .map_err(|_| ConversionError::InvalidPath)?;
103 98
104 progress.set_message("Create file"); 99 progress.set_message("Create file");
105 let mut file = File::create(&filestr)?; 100 let mut file = File::create(&filestr)?;
@@ -124,10 +119,10 @@ pub async fn import(
124 let list = List { 119 let list = List {
125 id: exportlist.id, 120 id: exportlist.id,
126 mc_version: exportlist.mc_version, 121 mc_version: exportlist.mc_version,
127 modloader: Modloader::from(&exportlist.launcher)?, 122 modloader: Modloader::try_from(exportlist.launcher.as_str())?,
128 download_folder: exportlist 123 download_folder: exportlist
129 .download_folder 124 .download_folder
130 .ok_or(MLErr::new(EType::Other, "NO_DL"))?, 125 .ok_or(Error::NoDownloadFolder)?,
131 }; 126 };
132 lists_insert( 127 lists_insert(
133 config, 128 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 @@
1use indicatif::{ProgressBar, ProgressStyle}; 1use indicatif::{ProgressBar, ProgressStyle};
2 2
3use crate::{ 3use crate::{
4 config::Cfg, data::modloader::Modloader, db::{ 4 config::Cfg,
5 config_change_current_list, lists_get, 5 data::modloader::Modloader,
6 lists_get_all_ids, lists_insert, lists_remove, lists_version, 6 db::{
7 }, error::{EType, MLErr, MLE}, update, STYLE_OPERATION 7 config_change_current_list, lists_get, lists_get_all_ids, lists_insert,
8 lists_remove, lists_version,
9 },
10 errors::{Error, MLE},
11 update, STYLE_OPERATION,
8}; 12};
9 13
10/// # Errors 14/// # Errors
@@ -16,11 +20,7 @@ pub fn add(
16 directory: &str, 20 directory: &str,
17) -> MLE<()> { 21) -> MLE<()> {
18 let p = ProgressBar::new_spinner(); 22 let p = ProgressBar::new_spinner();
19 p.set_style( 23 p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?);
20 ProgressStyle::with_template(STYLE_OPERATION).map_err(|_| {
21 MLErr::new(EType::LibIndicatif, "template error")
22 })?,
23 );
24 p.set_message(format!("Create {id}")); 24 p.set_message(format!("Create {id}"));
25 lists_insert(config, id, mc_version, modloader, directory)?; 25 lists_insert(config, id, mc_version, modloader, directory)?;
26 p.finish_with_message(format!("Created {id}")); 26 p.finish_with_message(format!("Created {id}"));
@@ -30,15 +30,11 @@ pub fn add(
30/// # Errors 30/// # Errors
31pub fn change(config: &Cfg, id: &str) -> MLE<()> { 31pub fn change(config: &Cfg, id: &str) -> MLE<()> {
32 let p = ProgressBar::new_spinner(); 32 let p = ProgressBar::new_spinner();
33 p.set_style( 33 p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?);
34 ProgressStyle::with_template(STYLE_OPERATION).map_err(|_| {
35 MLErr::new(EType::LibIndicatif, "template error")
36 })?,
37 );
38 p.set_message(format!("Change default list to {id}")); 34 p.set_message(format!("Change default list to {id}"));
39 35
40 if !lists_get_all_ids(config)?.into_iter().any(|l| l == id) { 36 if !lists_get_all_ids(config)?.into_iter().any(|l| l == id) {
41 return Err(MLErr::new(EType::ArgumentError, "List not found")); 37 return Err(Error::ListNotFound);
42 }; 38 };
43 config_change_current_list(config, id)?; 39 config_change_current_list(config, id)?;
44 40
@@ -49,11 +45,7 @@ pub fn change(config: &Cfg, id: &str) -> MLE<()> {
49/// # Errors 45/// # Errors
50pub fn remove(config: &Cfg, id: &str) -> MLE<()> { 46pub fn remove(config: &Cfg, id: &str) -> MLE<()> {
51 let p = ProgressBar::new_spinner(); 47 let p = ProgressBar::new_spinner();
52 p.set_style( 48 p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?);
53 ProgressStyle::with_template(STYLE_OPERATION).map_err(|_| {
54 MLErr::new(EType::LibIndicatif, "template error")
55 })?,
56 );
57 p.set_message(format!("Remove {id}")); 49 p.set_message(format!("Remove {id}"));
58 lists_remove(config, id)?; 50 lists_remove(config, id)?;
59 p.finish_with_message(format!("Removed {id}")); 51 p.finish_with_message(format!("Removed {id}"));
@@ -75,11 +67,7 @@ pub async fn version(
75 delete: bool, 67 delete: bool,
76) -> MLE<()> { 68) -> MLE<()> {
77 let p = ProgressBar::new_spinner(); 69 let p = ProgressBar::new_spinner();
78 p.set_style( 70 p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?);
79 ProgressStyle::with_template(STYLE_OPERATION).map_err(|_| {
80 MLErr::new(EType::LibIndicatif, "template error")
81 })?,
82 );
83 p.set_message(format!( 71 p.set_message(format!(
84 "Change version for list {id} to minecraft version: {mc_version}" 72 "Change version for list {id} to minecraft version: {mc_version}"
85 )); 73 ));
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;
3use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; 3use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
4 4
5use crate::{ 5use crate::{
6 apis::modrinth::{extract_current_version, get_raw_versions, project, projects, versions, Version}, config::Cfg, data::{modification::{AddMod, IDSelector}, project::ProjectInfo}, db::{ 6 apis::modrinth::{
7 extract_current_version, get_raw_versions, project, projects, versions,
8 Version,
9 },
10 config::Cfg,
11 data::{
12 modification::{AddMod, IDSelector},
13 projectinfo::ProjectInfo,
14 },
15 db::{
7 lists_get_all_ids, mods_get_id, mods_get_info, mods_insert, 16 lists_get_all_ids, mods_get_id, mods_get_info, mods_insert,
8 mods_remove, userlist_get_all_ids, userlist_get_current_version, 17 mods_remove, userlist_get_all_ids, userlist_get_current_version,
9 userlist_insert, userlist_remove, 18 userlist_insert, userlist_remove,
10 }, error::{EType, MLErr, MLE}, files::{delete_version, download_versions}, List, PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION 19 },
20 errors::{Error, MLE},
21 files::{delete_version, download_versions},
22 List, PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION,
11}; 23};
12 24
13/// # Errors 25/// # Errors
@@ -22,14 +34,9 @@ pub async fn mod_add(
22 let mut mod_ids: Vec<(String, bool)> = Vec::new(); 34 let mut mod_ids: Vec<(String, bool)> = Vec::new();
23 let mut ver_ids: Vec<(String, bool)> = Vec::new(); 35 let mut ver_ids: Vec<(String, bool)> = Vec::new();
24 36
25 let add_p = mp.add(ProgressBar::new( 37 let add_p = mp.add(ProgressBar::new(mods.len().try_into()?));
26 mods.len()
27 .try_into()
28 .map_err(|_| MLErr::new(EType::Other, "MODSLENTRY"))?,
29 ));
30 add_p.set_style( 38 add_p.set_style(
31 ProgressStyle::with_template(STYLE_BAR_POS) 39 ProgressStyle::with_template(STYLE_BAR_POS)?
32 .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
33 .progress_chars(PROGRESS_CHARS), 40 .progress_chars(PROGRESS_CHARS),
34 ); 41 );
35 add_p.set_message("Sort ids"); 42 add_p.set_message("Sort ids");
@@ -57,7 +64,7 @@ pub async fn mod_add(
57 }; 64 };
58 65
59 if projectinfo.is_empty() { 66 if projectinfo.is_empty() {
60 return Err(MLErr::new(EType::ArgumentError, "NO_IDS?")); 67 return Err(Error::NoProjectInfo);
61 }; 68 };
62 69
63 add_p.set_message("Add mods to database"); 70 add_p.set_message("Add mods to database");
@@ -65,29 +72,18 @@ pub async fn mod_add(
65 let mut downloadstack: Vec<Version> = Vec::new(); 72 let mut downloadstack: Vec<Version> = Vec::new();
66 73
67 //Adding each mod to the lists and downloadstack 74 //Adding each mod to the lists and downloadstack
68 let project_p = mp.insert_before( 75 let project_p = mp
69 &add_p, 76 .insert_before(&add_p, ProgressBar::new(projectinfo.len().try_into()?));
70 ProgressBar::new(
71 projectinfo
72 .len()
73 .try_into()
74 .map_err(|_| MLErr::new(EType::Other, "infolen"))?,
75 ),
76 );
77 project_p.set_style( 77 project_p.set_style(
78 ProgressStyle::with_template(STYLE_BAR_POS) 78 ProgressStyle::with_template(STYLE_BAR_POS)?
79 .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
80 .progress_chars(PROGRESS_CHARS), 79 .progress_chars(PROGRESS_CHARS),
81 ); 80 );
82 81
83 for project in projectinfo { 82 for project in projectinfo {
84 add_project(config, &project_p, &project, &list)?; 83 add_project(config, &project_p, &project, &list)?;
85 if project.current_version.is_some() { 84 if project.current_version.is_some() {
86 downloadstack.push( 85 downloadstack
87 project 86 .push(project.current_version.ok_or(Error::NoCurrentVersion)?);
88 .current_version
89 .ok_or(MLErr::new(EType::Other, "cur_ver"))?,
90 );
91 }; 87 };
92 } 88 }
93 89
@@ -125,7 +121,7 @@ fn add_project(
125 project 121 project
126 .current_version 122 .current_version
127 .clone() 123 .clone()
128 .ok_or(MLErr::new(EType::Other, "cur_ver"))? 124 .ok_or(Error::NoCurrentVersion)?
129 .id 125 .id
130 }; 126 };
131 127
@@ -142,9 +138,9 @@ fn add_project(
142 let expected_err = 138 let expected_err =
143 format!("SQL: UNIQUE constraint failed: {}.mod_id", list.id); 139 format!("SQL: UNIQUE constraint failed: {}.mod_id", list.id);
144 if e.to_string() == expected_err { 140 if e.to_string() == expected_err {
145 Err(MLErr::new(EType::ModError, "MOD_ALREADY_ON_SELECTED_LIST")) 141 Err(Error::ModAlreadyOnList)
146 } else { 142 } else {
147 Err(e) 143 Err(e)?
148 } 144 }
149 } 145 }
150 Ok(..) => Ok(..), 146 Ok(..) => Ok(..),
@@ -212,7 +208,7 @@ async fn get_mod_infos(
212 download_link: file, 208 download_link: file,
213 set_version: *setmap 209 set_version: *setmap
214 .get(&project.id) 210 .get(&project.id)
215 .ok_or(MLErr::new(EType::Other, "not in setmap"))?, 211 .ok_or(Error::VersionSetNotSet)?,
216 }); 212 });
217 } else { 213 } else {
218 let current_id = 214 let current_id =
@@ -312,10 +308,7 @@ async fn get_ver_info(
312/// # Errors 308/// # Errors
313pub fn mod_remove(config: &Cfg, id: &str, list: &List) -> MLE<()> { 309pub fn mod_remove(config: &Cfg, id: &str, list: &List) -> MLE<()> {
314 let progress = ProgressBar::new_spinner(); 310 let progress = ProgressBar::new_spinner();
315 progress.set_style( 311 progress.set_style(ProgressStyle::with_template(STYLE_OPERATION)?);
316 ProgressStyle::with_template(STYLE_OPERATION)
317 .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?,
318 );
319 312
320 let mod_id = mods_get_id(&config.data, id)?; 313 let mod_id = mods_get_id(&config.data, id)?;
321 314
@@ -351,7 +344,7 @@ pub fn mod_remove(config: &Cfg, id: &str, list: &List) -> MLE<()> {
351 if err.to_string() == "Database: NO_MODS_USERLIST" { 344 if err.to_string() == "Database: NO_MODS_USERLIST" {
352 return Ok(()); 345 return Ok(());
353 }; 346 };
354 return Err(err); 347 return Err(err)?;
355 } 348 }
356 }; 349 };
357 if mods.contains(&mod_id) { 350 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 @@
1use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; 1use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
2 2
3use crate::{ 3use crate::{
4 apis::modrinth::{extract_current_version, versions, Version}, config::Cfg, data::list::List, db::{ 4 apis::modrinth::{extract_current_version, versions, Version},
5 config::Cfg,
6 data::list::List,
7 db::{
5 mods_get_info, userlist_change_versions, userlist_get_all_ids, 8 mods_get_info, userlist_change_versions, userlist_get_all_ids,
6 userlist_get_applicable_versions, userlist_get_current_version, 9 userlist_get_applicable_versions, userlist_get_current_version,
7 userlist_get_set_version, 10 userlist_get_set_version,
8 }, error::{EType, MLErr, MLE}, files::{ 11 },
12 errors::{Error, MLE},
13 files::{
9 clean_list_dir, delete_version, disable_version, download_versions, 14 clean_list_dir, delete_version, disable_version, download_versions,
10 }, PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION 15 },
16 PROGRESS_CHARS, STYLE_BAR_POS, STYLE_OPERATION,
11}; 17};
12 18
13/// # Errors 19/// # Errors
@@ -20,15 +26,9 @@ pub async fn update(
20) -> MLE<()> { 26) -> MLE<()> {
21 let mp = MultiProgress::new(); 27 let mp = MultiProgress::new();
22 28
23 let update_p = mp.add(ProgressBar::new( 29 let update_p = mp.add(ProgressBar::new(liststack.len().try_into()?));
24 liststack
25 .len()
26 .try_into()
27 .map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?,
28 ));
29 update_p.set_style( 30 update_p.set_style(
30 ProgressStyle::with_template(STYLE_BAR_POS) 31 ProgressStyle::with_template(STYLE_BAR_POS)?
31 .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
32 .progress_chars(PROGRESS_CHARS), 32 .progress_chars(PROGRESS_CHARS),
33 ); 33 );
34 34
@@ -36,26 +36,15 @@ pub async fn update(
36 update_p.set_message(format!("Update {}", current_list.id)); 36 update_p.set_message(format!("Update {}", current_list.id));
37 37
38 let list_p = mp.insert_before(&update_p, ProgressBar::new(2)); 38 let list_p = mp.insert_before(&update_p, ProgressBar::new(2));
39 list_p.set_style( 39 list_p.set_style(ProgressStyle::with_template(STYLE_OPERATION)?);
40 ProgressStyle::with_template(STYLE_OPERATION).map_err(|_| {
41 MLErr::new(EType::LibIndicatif, "template error")
42 })?,
43 );
44 list_p.set_message("Update mods"); 40 list_p.set_message("Update mods");
45 41
46 let mods = userlist_get_all_ids(config, &current_list.id)?; 42 let mods = userlist_get_all_ids(config, &current_list.id)?;
47 43
48 let list_u_p = mp.insert_before( 44 let list_u_p =
49 &list_p, 45 mp.insert_before(&list_p, ProgressBar::new(mods.len().try_into()?));
50 ProgressBar::new(
51 mods.len()
52 .try_into()
53 .map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?,
54 ),
55 );
56 list_u_p.set_style( 46 list_u_p.set_style(
57 ProgressStyle::with_template(STYLE_BAR_POS) 47 ProgressStyle::with_template(STYLE_BAR_POS)?
58 .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
59 .progress_chars(PROGRESS_CHARS), 48 .progress_chars(PROGRESS_CHARS),
60 ); 49 );
61 50
@@ -63,7 +52,16 @@ pub async fn update(
63 let mut updatestack: Vec<Version> = vec![]; 52 let mut updatestack: Vec<Version> = vec![];
64 53
65 for id in mods { 54 for id in mods {
66 update_mod(config, id, list_u_p.clone(), &current_list, &mut updatestack, &mut current_versions, clean).await?; 55 update_mod(
56 config,
57 id,
58 list_u_p.clone(),
59 &current_list,
60 &mut updatestack,
61 &mut current_versions,
62 clean,
63 )
64 .await?;
67 } 65 }
68 66
69 list_u_p.finish_with_message(format!( 67 list_u_p.finish_with_message(format!(
@@ -91,12 +89,11 @@ pub async fn update(
91 let d_p = mp.insert_before( 89 let d_p = mp.insert_before(
92 &list_p, 90 &list_p,
93 ProgressBar::new( 91 ProgressBar::new(
94 current_versions.len().try_into().map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?, 92 current_versions.len().try_into()?,
95 ), 93 ),
96 ); 94 );
97 d_p.set_style( 95 d_p.set_style(
98 ProgressStyle::with_template(STYLE_BAR_POS) 96 ProgressStyle::with_template(STYLE_BAR_POS)?
99 .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
100 .progress_chars(PROGRESS_CHARS), 97 .progress_chars(PROGRESS_CHARS),
101 ); 98 );
102 for ver in current_versions { 99 for ver in current_versions {
@@ -129,45 +126,40 @@ pub async fn update(
129 Ok(()) 126 Ok(())
130} 127}
131 128
132async fn update_mod(config: &Cfg, id: String, list_u_p: ProgressBar, current_list: &List, updatestack: &mut Vec<Version>, current_versions: &mut Vec<(String, String)>, clean: bool) -> MLE<()> { 129async fn update_mod(
133 let info = mods_get_info(config, &id)?; 130 config: &Cfg,
134 list_u_p.set_message(format!("Update {}", info.title)); 131 id: String,
132 list_u_p: ProgressBar,
133 current_list: &List,
134 updatestack: &mut Vec<Version>,
135 current_versions: &mut Vec<(String, String)>,
136 clean: bool,
137) -> MLE<()> {
138 let info = mods_get_info(config, &id)?;
139 list_u_p.set_message(format!("Update {}", info.title));
135 140
136 //Skip check if version is set 141 //Skip check if version is set
137 if userlist_get_set_version(config, &current_list.id, &id)? { 142 if userlist_get_set_version(config, &current_list.id, &id)? {
138 list_u_p.inc(1); 143 list_u_p.inc(1);
139 return Ok(()); 144 return Ok(());
140 } 145 }
141 146
142 //Getting current installed version for disable or delete 147 //Getting current installed version for disable or delete
143 let disable_version = 148 let disable_version =
144 userlist_get_current_version(config, &current_list.id, &id)?; 149 userlist_get_current_version(config, &current_list.id, &id)?;
145 150
146 updatestack.push( 151 let version = specific_update(
147 match specific_update( 152 config,
148 config, 153 clean,
149 clean, 154 current_list.clone(),
150 current_list.clone(), 155 &id,
151 &id, 156 &list_u_p,
152 &list_u_p, 157 ).await?;
153 ) 158 if let Some(v) = version {
154 .await 159 updatestack.push(v);
155 { 160 current_versions.push((disable_version, id.to_string()));
156 Ok(ver) => { 161 }
157 current_versions.push((disable_version, id.to_string())); 162 list_u_p.inc(1);
158 ver
159 }
160 Err(e) => {
161 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" {
162 } else {
163 return Err(e);
164 };
165 list_u_p.inc(1);
166 return Ok(());
167 }
168 },
169 );
170 list_u_p.inc(1);
171 163
172 Ok(()) 164 Ok(())
173} 165}
@@ -178,7 +170,7 @@ async fn specific_update(
178 list: List, 170 list: List,
179 id: &str, 171 id: &str,
180 progress: &ProgressBar, 172 progress: &ProgressBar,
181) -> MLE<Version> { 173) -> MLE<Option<Version>> {
182 let applicable_versions = 174 let applicable_versions =
183 versions(&config.apis.modrinth, String::from(id), list.clone()).await?; 175 versions(&config.apis.modrinth, String::from(id), list.clone()).await?;
184 176
@@ -192,14 +184,12 @@ async fn specific_update(
192 } 184 }
193 } 185 }
194 186
195 let mut current: Vec<Version> = vec![]; 187 let mut current: Option<Version> = None;
196 if clean 188 if clean || (versions.join("|") != userlist_get_applicable_versions(
197 || (versions.join("|")
198 != userlist_get_applicable_versions(
199 config, 189 config,
200 &list.id, 190 &list.id,
201 String::from(id), 191 String::from(id),
202 )?) 192 )?)
203 { 193 {
204 let current_str = extract_current_version(applicable_versions.clone())?; 194 let current_str = extract_current_version(applicable_versions.clone())?;
205 195
@@ -211,15 +201,10 @@ async fn specific_update(
211 } 201 }
212 202
213 //get new versions 203 //get new versions
214 let current_ver = match applicable_versions 204 let Some(current_ver) = applicable_versions.into_iter().find(|ver| ver.id == current_str) else {
215 .into_iter() 205 return Err(Error::NoCurrentVersion);
216 .find(|ver| ver.id == current_str) 206 };
217 .ok_or("!no current version in applicable_versions") 207 current = Some(current_ver.clone());
218 {
219 Ok(v) => Ok(v),
220 Err(e) => Err(MLErr::new(EType::Other, e)),
221 }?;
222 current.push(current_ver.clone());
223 208
224 let files = &current_ver.files; 209 let files = &current_ver.files;
225 210
@@ -237,11 +222,12 @@ async fn specific_update(
237 link, 222 link,
238 id.to_string(), 223 id.to_string(),
239 )?; 224 )?;
240 }
241
242 if current.is_empty() {
243 return Err(MLErr::new(EType::ModError, "NO_UPDATE_AVAILABLE"));
244 }; 225 };
245 226
246 Ok(current[0].clone()) 227 if current.is_none() {
228 // No Update Available
229 Ok(None)
230 } else {
231 Ok(current)
232 }
247} 233}
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::{
7use serde::{Deserialize, Serialize}; 7use serde::{Deserialize, Serialize};
8 8
9use crate::{ 9use crate::{
10 data::{gameversion::{check_game_versions, VersionLevel}, modloader::Modloader}, db::setup, error::{EType, MLErr, MLE} 10 data::{
11 gameversion::{check_game_versions, VersionLevel},
12 modloader::Modloader,
13 },
14 db::setup,
15 errors::{Error, MLE},
11}; 16};
12 17
13#[derive(Debug, Clone, Serialize, Deserialize)] 18#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -55,14 +60,17 @@ pub struct Defaults {
55impl Cfg { 60impl Cfg {
56 /// # Errors 61 /// # Errors
57 pub async fn init(path: Option<String>) -> MLE<Self> { 62 pub async fn init(path: Option<String>) -> MLE<Self> {
58 let configfile = match path.clone() { 63 let configfile = if let Some(cf) = path.clone() {
59 Some(p) => p, 64 cf
60 None => dirs::config_dir() 65 } else {
61 .ok_or(MLErr::new(EType::Other, "config_dir"))? 66 let Some(config_dir) = dirs::config_dir() else {
67 return Err(Error::SysDirNotFound("config".to_string()));
68 };
69 config_dir
62 .join("modlist") 70 .join("modlist")
63 .join("config.toml") 71 .join("config.toml")
64 .to_string_lossy() 72 .to_string_lossy()
65 .to_string(), 73 .to_string()
66 }; 74 };
67 75
68 if let Some(err) = File::open(&configfile).err() { 76 if let Some(err) = File::open(&configfile).err() {
@@ -78,10 +86,8 @@ impl Cfg {
78 .add_source( 86 .add_source(
79 config::Environment::with_prefix("MODLIST").separator("_"), 87 config::Environment::with_prefix("MODLIST").separator("_"),
80 ) 88 )
81 .build() 89 .build()?
82 .unwrap() 90 .try_deserialize()?;
83 .try_deserialize()
84 .unwrap();
85 91
86 //Check cache 92 //Check cache
87 if !Path::new(&config.cache).exists() { 93 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 @@
1pub mod list; 1pub mod list;
2pub mod gameversion; 2pub mod gameversion;
3pub mod modloader; 3pub mod modloader;
4pub mod project; 4pub mod projectinfo;
5pub mod modification; 5pub mod modification;
6 6
7pub static STYLE_BAR_BYTE: &str = 7pub 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::{
5}; 5};
6 6
7use apis::modrinth::{get_game_versions, GameVersion, GameVersionType}; 7use apis::modrinth::{get_game_versions, GameVersion, GameVersionType};
8use error::{EType, MLErr, MLE}; 8use errors::MLE;
9use indicatif::{ProgressBar, ProgressStyle}; 9use indicatif::{ProgressBar, ProgressStyle};
10use serde::{Deserialize, Serialize}; 10use serde::{Deserialize, Serialize};
11 11
12use crate::{apis, error, STYLE_MESSAGE}; 12use crate::{
13 apis,
14 errors::{self, Error},
15 STYLE_MESSAGE,
16};
13 17
14#[derive(Debug, Clone, Deserialize, Serialize)] 18#[derive(Debug, Clone, Deserialize, Serialize)]
15pub enum VersionLevel { 19pub enum VersionLevel {
@@ -25,15 +29,11 @@ pub enum VersionLevel {
25/// # Errors 29/// # Errors
26pub async fn check_game_versions(path: &str, force: bool) -> MLE<()> { 30pub async fn check_game_versions(path: &str, force: bool) -> MLE<()> {
27 let p = ProgressBar::new(1); 31 let p = ProgressBar::new(1);
28 p.set_style(ProgressStyle::with_template(STYLE_MESSAGE).map_err(|_| { 32 p.set_style(ProgressStyle::with_template(STYLE_MESSAGE)?);
29 MLErr::new(EType::LibIndicatif, "template error")
30 })?);
31 p.set_message("Update minecraft versions"); 33 p.set_message("Update minecraft versions");
32 34
33 let creation_time = fs::metadata(path)?.created()?; 35 let creation_time = fs::metadata(path)?.created()?;
34 if !force 36 if !force && creation_time.elapsed()? < Duration::from_secs(60 * 60 * 24) {
35 && creation_time.elapsed().map_err(|_| MLErr::new(EType::LibIndicatif, "SystemTimeError"))? < Duration::from_secs(60 * 60 * 24)
36 {
37 return Ok(()); 37 return Ok(());
38 } 38 }
39 39
@@ -57,7 +57,7 @@ pub fn load_game_versions(path: &str) -> MLE<Vec<GameVersion>> {
57} 57}
58 58
59impl VersionLevel { 59impl VersionLevel {
60 pub fn from(str: &str) -> Self { 60 #[must_use] pub fn from(str: &str) -> Self {
61 match str { 61 match str {
62 "release" => VersionLevel::Release, 62 "release" => VersionLevel::Release,
63 "snapshot" => VersionLevel::Snapshot, 63 "snapshot" => VersionLevel::Snapshot,
@@ -85,10 +85,7 @@ impl VersionLevel {
85 { 85 {
86 Ok(release.version) 86 Ok(release.version)
87 } else { 87 } else {
88 Err(MLErr::new( 88 Err(Error::MinecraftVersionNotFound)
89 EType::Other,
90 "no minecraft release version found",
91 ))
92 } 89 }
93 } 90 }
94 VersionLevel::Snapshot => { 91 VersionLevel::Snapshot => {
@@ -97,20 +94,14 @@ impl VersionLevel {
97 { 94 {
98 Ok(snapshot.version) 95 Ok(snapshot.version)
99 } else { 96 } else {
100 Err(MLErr::new( 97 Err(Error::MinecraftVersionNotFound)
101 EType::Other,
102 "no minecraft snapshot version found",
103 ))
104 } 98 }
105 } 99 }
106 VersionLevel::Version(v) => { 100 VersionLevel::Version(v) => {
107 if versions.any(|ver| ver.version == v) { 101 if versions.any(|ver| ver.version == v) {
108 Ok(v) 102 Ok(v)
109 } else { 103 } else {
110 Err(MLErr::new( 104 Err(Error::MinecraftVersionNotFound)
111 EType::ConfigError,
112 "unknown minecraft version",
113 ))
114 } 105 }
115 } 106 }
116 } 107 }
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 @@
1use crate::{config::Cfg, db::{config_get_current_list, lists_get}, error::MLE}; 1use crate::{config::Cfg, db::{config_get_current_list, lists_get}, errors::MLE};
2 2
3use super::modloader::Modloader; 3use super::modloader::Modloader;
4 4
@@ -14,6 +14,6 @@ impl List {
14 /// # Errors 14 /// # Errors
15 pub fn get_current_list(config: &Cfg) -> MLE<List> { 15 pub fn get_current_list(config: &Cfg) -> MLE<List> {
16 let id = config_get_current_list(config)?; 16 let id = config_get_current_list(config)?;
17 lists_get(config, &id) 17 Ok(lists_get(config, &id)?)
18 } 18 }
19} 19}
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;
2 2
3use serde::{Deserialize, Serialize}; 3use serde::{Deserialize, Serialize};
4 4
5use crate::error::{EType, MLErr, MLE}; 5use crate::errors::ConversionError;
6 6
7#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] 7#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
8pub enum Modloader { 8pub enum Modloader {
@@ -14,16 +14,14 @@ pub enum Modloader {
14 Quilt, 14 Quilt,
15} 15}
16 16
17impl Modloader { 17impl TryFrom<&str> for Modloader {
18 /// # Errors 18 type Error = ConversionError;
19 pub fn from(string: &str) -> MLE<Modloader> { 19 fn try_from(value: &str) -> Result<Self, Self::Error> {
20 match string { 20 match value {
21 "forge" => Ok(Modloader::Forge), 21 "forge" => Ok(Modloader::Forge),
22 "fabric" => Ok(Modloader::Fabric), 22 "fabric" => Ok(Modloader::Fabric),
23 "quilt" => Ok(Modloader::Quilt), 23 "quilt" => Ok(Modloader::Quilt),
24 _ => { 24 _ => Err(ConversionError::Modloader(value.to_string()))
25 Err(MLErr::new(EType::ArgumentError, "UNKNOWN_MODLOADER"))
26 }
27 } 25 }
28 } 26 }
29} 27}
diff --git a/src/data/project.rs b/src/data/projectinfo.rs
index 9807867..9807867 100644
--- a/src/data/project.rs
+++ b/src/data/projectinfo.rs
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 @@
1use std::io::{Error, ErrorKind};
2
3use rusqlite::Connection; 1use rusqlite::Connection;
4 2
5use crate::{ 3use crate::{
6 config::Cfg, 4 config::Cfg,
7 data::{list::List, modloader::Modloader}, 5 data::{list::List, modloader::Modloader},
8 error::{EType, MLErr, MLE},
9}; 6};
10 7
8type DbE<T> = Result<T, DBError>;
9
10#[derive(Debug, thiserror::Error)]
11pub enum DBError {
12 #[error("sqlite: {source}")]
13 Sqlite {
14 #[from]
15 source: rusqlite::Error,
16 },
17
18 #[error("conversion: {source}")]
19 Conversion {
20 #[from]
21 source: crate::errors::ConversionError,
22 },
23
24 #[error("not enough arguments")]
25 NotEnoughArguments,
26
27 #[error("couldn't find requested mod/version")]
28 NotFound,
29
30 #[error("no mods/versions are available in the requested scope")]
31 NotAvailable,
32}
33
11//MODS 34//MODS
12/// # Errors 35/// # Errors
13pub fn mods_insert(config: &Cfg, id: &str, slug: &str, name: &str) -> MLE<()> { 36pub fn mods_insert(config: &Cfg, id: &str, slug: &str, name: &str) -> DbE<()> {
14 let data = format!("{}/data.db", config.data); 37 let data = format!("{}/data.db", config.data);
15 let connection = Connection::open(data)?; 38 let connection = Connection::open(data)?;
16 39
@@ -23,9 +46,7 @@ pub fn mods_insert(config: &Cfg, id: &str, slug: &str, name: &str) -> MLE<()> {
23} 46}
24 47
25/// # Errors 48/// # Errors
26pub fn mods_get_all_ids( 49pub fn mods_get_all_ids(config: &Cfg) -> DbE<Vec<String>> {
27 config: &Cfg,
28) -> Result<Vec<String>, Box<dyn std::error::Error>> {
29 let data = format!("{}/data.db", config.data); 50 let data = format!("{}/data.db", config.data);
30 let connection = Connection::open(data)?; 51 let connection = Connection::open(data)?;
31 52
@@ -39,7 +60,7 @@ pub fn mods_get_all_ids(
39 } 60 }
40 61
41 if mods.is_empty() { 62 if mods.is_empty() {
42 Err(Box::new(Error::new(ErrorKind::NotFound, "NO_MODS_ALL"))) 63 Err(DBError::NotFound)
43 } else { 64 } else {
44 Ok(mods) 65 Ok(mods)
45 } 66 }
@@ -53,8 +74,8 @@ pub fn mods_get_all_ids(
53/// 74///
54/// # Errors 75/// # Errors
55/// 76///
56/// Will return `MLError` when no mod id is found 77/// Will return `DBError` when no mod id is found
57pub fn mods_get_id(data: &str, slug: &str) -> MLE<String> { 78pub fn mods_get_id(data: &str, slug: &str) -> DbE<String> {
58 let data = format!("{data}/data.db"); 79 let data = format!("{data}/data.db");
59 let connection = Connection::open(data)?; 80 let connection = Connection::open(data)?;
60 81
@@ -91,7 +112,7 @@ pub fn mods_get_id(data: &str, slug: &str) -> MLE<String> {
91 } 112 }
92 113
93 if mod_id.is_empty() { 114 if mod_id.is_empty() {
94 return Err(MLErr::new(EType::DBError, "GI_MOD_NOT_FOUND")); 115 Err(DBError::NotFound)?;
95 }; 116 };
96 117
97 Ok(mod_id) 118 Ok(mod_id)
@@ -103,7 +124,7 @@ pub struct ModInfo {
103} 124}
104 125
105/// # Errors 126/// # Errors
106pub fn mods_get_info(config: &Cfg, id: &str) -> MLE<ModInfo> { 127pub fn mods_get_info(config: &Cfg, id: &str) -> DbE<ModInfo> {
107 let data = format!("{}/data.db", config.data); 128 let data = format!("{}/data.db", config.data);
108 let connection = Connection::open(data)?; 129 let connection = Connection::open(data)?;
109 130
@@ -125,15 +146,15 @@ pub fn mods_get_info(config: &Cfg, id: &str) -> MLE<ModInfo> {
125 }); 146 });
126 } 147 }
127 148
128 if mod_info.is_none() { 149 if let Some(mi) = mod_info {
129 Err(MLErr::new(EType::DBError, "GN_MOD_NOT_FOUND")) 150 Ok(mi)
130 } else { 151 } else {
131 Ok(mod_info.ok_or(MLErr::new(EType::Other, "mod_info"))?) 152 Err(DBError::NotFound)?
132 } 153 }
133} 154}
134 155
135/// # Errors 156/// # Errors
136pub fn mods_remove(config: &Cfg, id: &str) -> MLE<()> { 157pub fn mods_remove(config: &Cfg, id: &str) -> DbE<()> {
137 let data = format!("{}/data.db", config.data); 158 let data = format!("{}/data.db", config.data);
138 let connection = Connection::open(data)?; 159 let connection = Connection::open(data)?;
139 160
@@ -152,12 +173,12 @@ pub struct DBModlistVersions {
152pub fn mods_get_versions( 173pub fn mods_get_versions(
153 config: &Cfg, 174 config: &Cfg,
154 mods: &[String], 175 mods: &[String],
155) -> MLE<Vec<DBModlistVersions>> { 176) -> DbE<Vec<DBModlistVersions>> {
156 let data = format!("{}/data.db", config.data); 177 let data = format!("{}/data.db", config.data);
157 let connection = Connection::open(data)?; 178 let connection = Connection::open(data)?;
158 179
159 if mods.is_empty() { 180 if mods.is_empty() {
160 return Err(MLErr::new(EType::ArgumentError, "MODS_NO_INPUT")); 181 Err(DBError::NotEnoughArguments)?;
161 } 182 }
162 183
163 let mut wherestr = String::from("WHERE"); 184 let mut wherestr = String::from("WHERE");
@@ -190,7 +211,7 @@ pub fn mods_get_versions(
190 } 211 }
191 212
192 if versionmaps.is_empty() { 213 if versionmaps.is_empty() {
193 Err(MLErr::new(EType::DBError, "MODS_MODS_NOT_FOUND")) 214 Err(DBError::NotFound)
194 } else { 215 } else {
195 Ok(versionmaps) 216 Ok(versionmaps)
196 } 217 }
@@ -206,7 +227,7 @@ pub fn userlist_insert(
206 applicable_versions: &[String], 227 applicable_versions: &[String],
207 current_link: &str, 228 current_link: &str,
208 set_version: bool, 229 set_version: bool,
209) -> MLE<()> { 230) -> DbE<()> {
210 let data = format!("{}/data.db", config.data); 231 let data = format!("{}/data.db", config.data);
211 let connection = Connection::open(data)?; 232 let connection = Connection::open(data)?;
212 233
@@ -228,7 +249,7 @@ pub fn userlist_insert(
228} 249}
229 250
230/// # Errors 251/// # Errors
231pub fn userlist_get_all_ids(config: &Cfg, list_id: &str) -> MLE<Vec<String>> { 252pub fn userlist_get_all_ids(config: &Cfg, list_id: &str) -> DbE<Vec<String>> {
232 let data = format!("{}/data.db", config.data); 253 let data = format!("{}/data.db", config.data);
233 let connection = Connection::open(data)?; 254 let connection = Connection::open(data)?;
234 255
@@ -242,17 +263,14 @@ pub fn userlist_get_all_ids(config: &Cfg, list_id: &str) -> MLE<Vec<String>> {
242 } 263 }
243 264
244 if mod_ids.is_empty() { 265 if mod_ids.is_empty() {
245 Err(MLErr::new( 266 Err(DBError::NotAvailable)
246 EType::DBError,
247 &format!("NO_MODS_USERLIST{list_id}"),
248 ))
249 } else { 267 } else {
250 Ok(mod_ids) 268 Ok(mod_ids)
251 } 269 }
252} 270}
253 271
254/// # Errors 272/// # Errors
255pub fn userlist_remove(config: &Cfg, list_id: &str, mod_id: &str) -> MLE<()> { 273pub fn userlist_remove(config: &Cfg, list_id: &str, mod_id: &str) -> DbE<()> {
256 let data = format!("{}/data.db", config.data); 274 let data = format!("{}/data.db", config.data);
257 let connection = Connection::open(data)?; 275 let connection = Connection::open(data)?;
258 276
@@ -268,7 +286,7 @@ pub fn userlist_get_applicable_versions(
268 config: &Cfg, 286 config: &Cfg,
269 list_id: &str, 287 list_id: &str,
270 mod_id: String, 288 mod_id: String,
271) -> MLE<String> { 289) -> DbE<String> {
272 let data = format!("{}/data.db", config.data); 290 let data = format!("{}/data.db", config.data);
273 let connection = Connection::open(data)?; 291 let connection = Connection::open(data)?;
274 292
@@ -285,7 +303,7 @@ pub fn userlist_get_applicable_versions(
285 } 303 }
286 304
287 if version.is_empty() { 305 if version.is_empty() {
288 Err(MLErr::new(EType::DBError, "GAV_MOD_NOT_FOUND")) 306 Err(DBError::NotFound)
289 } else { 307 } else {
290 Ok(version) 308 Ok(version)
291 } 309 }
@@ -295,7 +313,7 @@ pub fn userlist_get_applicable_versions(
295pub fn userlist_get_all_applicable_versions_with_mods( 313pub fn userlist_get_all_applicable_versions_with_mods(
296 config: &Cfg, 314 config: &Cfg,
297 list_id: &str, 315 list_id: &str,
298) -> MLE<Vec<(String, String)>> { 316) -> DbE<Vec<(String, String)>> {
299 let data = format!("{}/data.db", config.data); 317 let data = format!("{}/data.db", config.data);
300 let connection = Connection::open(data)?; 318 let connection = Connection::open(data)?;
301 319
@@ -316,7 +334,7 @@ pub fn userlist_get_all_applicable_versions_with_mods(
316 } 334 }
317 335
318 if versions.is_empty() { 336 if versions.is_empty() {
319 return Err(MLErr::new(EType::DBError, "NO_MODS_ON_LIST")); 337 return Err(DBError::NotAvailable);
320 }; 338 };
321 339
322 Ok(versions) 340 Ok(versions)
@@ -327,7 +345,7 @@ pub fn userlist_get_current_version(
327 config: &Cfg, 345 config: &Cfg,
328 list_id: &str, 346 list_id: &str,
329 mod_id: &str, 347 mod_id: &str,
330) -> MLE<String> { 348) -> DbE<String> {
331 let data = format!("{}/data.db", config.data); 349 let data = format!("{}/data.db", config.data);
332 let connection = Connection::open(data)?; 350 let connection = Connection::open(data)?;
333 351
@@ -344,7 +362,7 @@ pub fn userlist_get_current_version(
344 } 362 }
345 363
346 if version.is_empty() { 364 if version.is_empty() {
347 Err(MLErr::new(EType::DBError, "GCV_MOD_NOT_FOUND")) 365 Err(DBError::NotFound)
348 } else { 366 } else {
349 Ok(version) 367 Ok(version)
350 } 368 }
@@ -354,7 +372,7 @@ pub fn userlist_get_current_version(
354pub fn userlist_get_all_current_version_ids( 372pub fn userlist_get_all_current_version_ids(
355 config: &Cfg, 373 config: &Cfg,
356 list_id: &str, 374 list_id: &str,
357) -> MLE<Vec<String>> { 375) -> DbE<Vec<String>> {
358 let data = format!("{}/data.db", config.data); 376 let data = format!("{}/data.db", config.data);
359 let connection = Connection::open(data)?; 377 let connection = Connection::open(data)?;
360 378
@@ -368,7 +386,7 @@ pub fn userlist_get_all_current_version_ids(
368 } 386 }
369 387
370 if versions.is_empty() { 388 if versions.is_empty() {
371 return Err(MLErr::new(EType::DBError, "NO_MODS_ON_LIST")); 389 return Err(DBError::NotAvailable);
372 }; 390 };
373 391
374 Ok(versions) 392 Ok(versions)
@@ -378,7 +396,7 @@ pub fn userlist_get_all_current_version_ids(
378pub fn userlist_get_all_current_versions_with_mods( 396pub fn userlist_get_all_current_versions_with_mods(
379 config: &Cfg, 397 config: &Cfg,
380 list_id: &str, 398 list_id: &str,
381) -> Result<Vec<(String, String)>, Box<dyn std::error::Error>> { 399) -> DbE<Vec<(String, String)>> {
382 let data = format!("{}/data.db", config.data); 400 let data = format!("{}/data.db", config.data);
383 let connection = Connection::open(data)?; 401 let connection = Connection::open(data)?;
384 402
@@ -399,10 +417,7 @@ pub fn userlist_get_all_current_versions_with_mods(
399 } 417 }
400 418
401 if versions.is_empty() { 419 if versions.is_empty() {
402 return Err(Box::new(std::io::Error::new( 420 return Err(DBError::NotAvailable);
403 ErrorKind::Other,
404 "NO_MODS_ON_LIST",
405 )));
406 }; 421 };
407 422
408 Ok(versions) 423 Ok(versions)
@@ -413,7 +428,7 @@ pub fn userlist_get_set_version(
413 config: &Cfg, 428 config: &Cfg,
414 list_id: &str, 429 list_id: &str,
415 mod_id: &str, 430 mod_id: &str,
416) -> MLE<bool> { 431) -> DbE<bool> {
417 let data = format!("{}/data.db", config.data); 432 let data = format!("{}/data.db", config.data);
418 let connection = Connection::open(data)?; 433 let connection = Connection::open(data)?;
419 434
@@ -439,7 +454,7 @@ pub fn userlist_change_versions(
439 versions: String, 454 versions: String,
440 link: String, 455 link: String,
441 mod_id: String, 456 mod_id: String,
442) -> MLE<()> { 457) -> DbE<()> {
443 let data = format!("{}/data.db", config.data); 458 let data = format!("{}/data.db", config.data);
444 let connection = Connection::open(data)?; 459 let connection = Connection::open(data)?;
445 460
@@ -453,7 +468,7 @@ pub fn userlist_add_disabled_versions(
453 list_id: &str, 468 list_id: &str,
454 disabled_version: String, 469 disabled_version: String,
455 mod_id: String, 470 mod_id: String,
456) -> MLE<()> { 471) -> DbE<()> {
457 let data = format!("{}/data.db", config.data); 472 let data = format!("{}/data.db", config.data);
458 let connection = Connection::open(data)?; 473 let connection = Connection::open(data)?;
459 474
@@ -480,7 +495,7 @@ pub fn userlist_get_disabled_versions(
480 config: &Cfg, 495 config: &Cfg,
481 list_id: &str, 496 list_id: &str,
482 mod_id: String, 497 mod_id: String,
483) -> MLE<String> { 498) -> DbE<String> {
484 let data = format!("{}/data.db", config.data); 499 let data = format!("{}/data.db", config.data);
485 let connection = Connection::open(data)?; 500 let connection = Connection::open(data)?;
486 501
@@ -497,7 +512,7 @@ pub fn userlist_get_disabled_versions(
497 } 512 }
498 513
499 if version.is_empty() { 514 if version.is_empty() {
500 Err(MLErr::new(EType::DBError, "GDV_MOD_NOT_FOUND")) 515 Err(DBError::NotFound)
501 } else { 516 } else {
502 Ok(version) 517 Ok(version)
503 } 518 }
@@ -507,7 +522,7 @@ pub fn userlist_get_disabled_versions(
507pub fn userlist_get_all_downloads( 522pub fn userlist_get_all_downloads(
508 config: &Cfg, 523 config: &Cfg,
509 list_id: &str, 524 list_id: &str,
510) -> Result<Vec<String>, Box<dyn std::error::Error>> { 525) -> DbE<Vec<String>> {
511 let data = format!("{}/data.db", config.data); 526 let data = format!("{}/data.db", config.data);
512 let connection = Connection::open(data)?; 527 let connection = Connection::open(data)?;
513 528
@@ -522,10 +537,7 @@ pub fn userlist_get_all_downloads(
522 } 537 }
523 538
524 if links.is_empty() { 539 if links.is_empty() {
525 return Err(Box::new(std::io::Error::new( 540 return Err(DBError::NotAvailable);
526 ErrorKind::Other,
527 "NO_MODS_ON_LIST",
528 )));
529 }; 541 };
530 542
531 Ok(links) 543 Ok(links)
@@ -540,7 +552,7 @@ pub fn lists_insert(
540 mc_version: &str, 552 mc_version: &str,
541 mod_loader: &Modloader, 553 mod_loader: &Modloader,
542 download_folder: &str, 554 download_folder: &str,
543) -> MLE<()> { 555) -> DbE<()> {
544 let data = format!("{}/data.db", config.data); 556 let data = format!("{}/data.db", config.data);
545 let connection = Connection::open(data)?; 557 let connection = Connection::open(data)?;
546 558
@@ -554,7 +566,7 @@ pub fn lists_insert(
554} 566}
555 567
556/// # Errors 568/// # Errors
557pub fn lists_remove(config: &Cfg, id: &str) -> MLE<()> { 569pub fn lists_remove(config: &Cfg, id: &str) -> DbE<()> {
558 let data = format!("{}/data.db", config.data); 570 let data = format!("{}/data.db", config.data);
559 let connection = Connection::open(data)?; 571 let connection = Connection::open(data)?;
560 572
@@ -564,7 +576,7 @@ pub fn lists_remove(config: &Cfg, id: &str) -> MLE<()> {
564} 576}
565 577
566/// # Errors 578/// # Errors
567pub fn lists_get(config: &Cfg, list_id: &str) -> MLE<List> { 579pub fn lists_get(config: &Cfg, list_id: &str) -> DbE<List> {
568 let data = format!("{}/data.db", config.data); 580 let data = format!("{}/data.db", config.data);
569 let connection = Connection::open(data)?; 581 let connection = Connection::open(data)?;
570 582
@@ -591,20 +603,20 @@ pub fn lists_get(config: &Cfg, list_id: &str) -> MLE<List> {
591 list = List { 603 list = List {
592 id: list_id.to_string(), 604 id: list_id.to_string(),
593 mc_version: String::from(&li[0]), 605 mc_version: String::from(&li[0]),
594 modloader: Modloader::from(&li[1])?, 606 modloader: Modloader::try_from(li[1].as_str())?,
595 download_folder: String::from(&li[2]), 607 download_folder: String::from(&li[2]),
596 }; 608 };
597 } 609 }
598 610
599 if list.id.is_empty() { 611 if list.id.is_empty() {
600 return Err(MLErr::new(EType::DBError, "LIST_NOT_FOUND")); 612 Err(DBError::NotFound)?;
601 } 613 }
602 614
603 Ok(list) 615 Ok(list)
604} 616}
605 617
606/// # Errors 618/// # Errors
607pub fn lists_version(config: &Cfg, list_id: &str, version: &str) -> MLE<()> { 619pub fn lists_version(config: &Cfg, list_id: &str, version: &str) -> DbE<()> {
608 let data = format!("{}/data.db", config.data); 620 let data = format!("{}/data.db", config.data);
609 let connection = Connection::open(data)?; 621 let connection = Connection::open(data)?;
610 622
@@ -616,7 +628,7 @@ pub fn lists_version(config: &Cfg, list_id: &str, version: &str) -> MLE<()> {
616} 628}
617 629
618/// # Errors 630/// # Errors
619pub fn lists_get_all_ids(config: &Cfg) -> MLE<Vec<String>> { 631pub fn lists_get_all_ids(config: &Cfg) -> DbE<Vec<String>> {
620 let data = format!("{}/data.db", config.data); 632 let data = format!("{}/data.db", config.data);
621 let connection = Connection::open(data)?; 633 let connection = Connection::open(data)?;
622 634
@@ -629,7 +641,7 @@ pub fn lists_get_all_ids(config: &Cfg) -> MLE<Vec<String>> {
629 } 641 }
630 642
631 if list_ids.is_empty() { 643 if list_ids.is_empty() {
632 Err(MLErr::new(EType::DBError, "NO_LISTS")) 644 Err(DBError::NotAvailable)
633 } else { 645 } else {
634 Ok(list_ids) 646 Ok(list_ids)
635 } 647 }
@@ -637,7 +649,7 @@ pub fn lists_get_all_ids(config: &Cfg) -> MLE<Vec<String>> {
637 649
638//config 650//config
639/// # Errors 651/// # Errors
640pub fn config_change_current_list(config: &Cfg, id: &str) -> MLE<()> { 652pub fn config_change_current_list(config: &Cfg, id: &str) -> DbE<()> {
641 let data = format!("{}/data.db", config.data); 653 let data = format!("{}/data.db", config.data);
642 let connection = Connection::open(data)?; 654 let connection = Connection::open(data)?;
643 655
@@ -649,7 +661,7 @@ pub fn config_change_current_list(config: &Cfg, id: &str) -> MLE<()> {
649} 661}
650 662
651/// # Errors 663/// # Errors
652pub fn config_get_current_list(config: &Cfg) -> MLE<String> { 664pub fn config_get_current_list(config: &Cfg) -> DbE<String> {
653 let data = format!("{}/data.db", config.data); 665 let data = format!("{}/data.db", config.data);
654 let connection = Connection::open(data)?; 666 let connection = Connection::open(data)?;
655 667
@@ -663,7 +675,7 @@ pub fn config_get_current_list(config: &Cfg) -> MLE<String> {
663 } 675 }
664 676
665 if list_id.is_empty() { 677 if list_id.is_empty() {
666 return Err(MLErr::new(EType::DBError, "NO_CURRENT_LIST")); 678 return Err(DBError::NotAvailable);
667 } 679 }
668 680
669 Ok(list_id) 681 Ok(list_id)
@@ -720,7 +732,7 @@ pub fn s_config_update_version(
720/// # Errors 732/// # Errors
721pub fn s_config_get_version( 733pub fn s_config_get_version(
722 config: &Cfg, 734 config: &Cfg,
723) -> Result<String, Box<dyn std::error::Error>> { 735) -> DbE<String> {
724 let data = format!("{}/data.db", config.data); 736 let data = format!("{}/data.db", config.data);
725 let connection = Connection::open(data)?; 737 let connection = Connection::open(data)?;
726 738
@@ -734,10 +746,7 @@ pub fn s_config_get_version(
734 } 746 }
735 747
736 if version.is_empty() { 748 if version.is_empty() {
737 return Err(Box::new(std::io::Error::new( 749 return Err(DBError::NotAvailable);
738 ErrorKind::Other,
739 "NO_DBVERSION",
740 )));
741 }; 750 };
742 Ok(version) 751 Ok(version)
743} 752}
@@ -749,18 +758,14 @@ pub fn s_insert_column(
749 column: &str, 758 column: &str,
750 c_type: &str, 759 c_type: &str,
751 default: Option<String>, 760 default: Option<String>,
752) -> Result<(), Box<dyn std::error::Error>> { 761) -> DbE<()> {
753 let data = format!("{}/data.db", config.data); 762 let data = format!("{}/data.db", config.data);
754 let connection = Connection::open(data)?; 763 let connection = Connection::open(data)?;
755 764
756 let mut sql = format!("ALTER TABLE {table} ADD '{column}' {c_type}"); 765 let mut sql = format!("ALTER TABLE {table} ADD '{column}' {c_type}");
757 766
758 if default.is_some() { 767 if let Some(def) = default {
759 sql = format!( 768 sql = format!("{sql} DEFAULT {def}");
760 "{} DEFAULT {}",
761 sql,
762 default.ok_or(MLErr::new(EType::Other, "errornous default"))?
763 );
764 } 769 }
765 770
766 connection.execute(sql.as_str(), ())?; 771 connection.execute(sql.as_str(), ())?;
@@ -768,7 +773,7 @@ pub fn s_insert_column(
768} 773}
769 774
770/// # Errors 775/// # Errors
771pub fn setup(path: &str) -> MLE<()> { 776pub fn setup(path: &str) -> DbE<()> {
772 let connection = Connection::open(path)?; 777 let connection = Connection::open(path)?;
773 778
774 connection.execute_batch( 779 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 @@
1use core::fmt;
2use serde::Deserialize;
3
4pub type MLE<T> = Result<T, MLErr>;
5
6#[derive(Debug, Deserialize)]
7pub struct MLErr {
8 etype: EType,
9 message: String,
10}
11
12#[derive(Debug, Deserialize)]
13pub enum EType {
14 ArgumentError,
15 ArgumentCountError,
16 ConfigError,
17 DBError,
18 ModError,
19 LibToml,
20 LibSql,
21 LibReq,
22 LibChrono,
23 LibJson,
24 LibIndicatif,
25 IoError,
26 Other,
27}
28
29impl std::error::Error for MLErr {
30 fn description(&self) -> &str {
31 &self.message
32 }
33}
34
35impl fmt::Display for MLErr {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 match self.etype {
38 EType::ArgumentError => {
39 write!(f, "User input not accepted: {}", self.message)
40 }
41 EType::ArgumentCountError => {
42 write!(f, "Too many/too few arguments")
43 }
44 EType::ConfigError => write!(f, "CONFIG"),
45 EType::DBError => write!(f, "Database: {}", self.message),
46 EType::ModError => write!(f, "Mod: {}", self.message),
47 EType::LibToml => write!(f, "TOML"),
48 EType::LibSql => write!(f, "SQL: {}", self.message),
49 EType::LibReq => write!(f, "REQWEST"),
50 EType::LibChrono => write!(f, "Chrono error: {}", self.message),
51 EType::LibJson => write!(f, "JSON: {}", self.message),
52 EType::LibIndicatif => write!(f, "Indicativ: {}", self.message),
53 EType::IoError => write!(f, "IO"),
54 EType::Other => write!(f, "OTHER"),
55 }
56 }
57}
58
59impl From<reqwest::Error> for MLErr {
60 fn from(error: reqwest::Error) -> Self {
61 Self {
62 etype: EType::LibReq,
63 message: error.to_string(),
64 }
65 }
66}
67
68impl From<toml::de::Error> for MLErr {
69 fn from(error: toml::de::Error) -> Self {
70 Self {
71 etype: EType::LibToml,
72 message: error.to_string(),
73 }
74 }
75}
76
77impl From<rusqlite::Error> for MLErr {
78 fn from(error: rusqlite::Error) -> Self {
79 Self {
80 etype: EType::LibSql,
81 message: error.to_string(),
82 }
83 }
84}
85
86impl From<toml::ser::Error> for MLErr {
87 fn from(error: toml::ser::Error) -> Self {
88 Self {
89 etype: EType::LibToml,
90 message: error.to_string(),
91 }
92 }
93}
94
95impl From<chrono::ParseError> for MLErr {
96 fn from(error: chrono::ParseError) -> Self {
97 Self {
98 etype: EType::LibChrono,
99 message: error.to_string(),
100 }
101 }
102}
103
104impl From<std::io::Error> for MLErr {
105 fn from(error: std::io::Error) -> Self {
106 Self {
107 etype: EType::IoError,
108 message: error.to_string(),
109 }
110 }
111}
112
113impl From<serde_json::error::Error> for MLErr {
114 fn from(value: serde_json::error::Error) -> Self {
115 Self {
116 etype: EType::LibJson,
117 message: value.to_string(),
118 }
119 }
120}
121
122impl MLErr {
123 #[must_use] pub fn new(etype: EType, message: &str) -> Self {
124 Self {
125 etype,
126 message: String::from(message),
127 }
128 }
129}
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 @@
1use std::{io, num::TryFromIntError, time::SystemTimeError};
2
3use chrono::ParseError;
4use indicatif::style::TemplateError;
5use reqwest::StatusCode;
6
7use crate::db::DBError;
8
9pub type MLE<T> = Result<T, Error>;
10
11#[derive(Debug, thiserror::Error)]
12pub enum Error {
13 #[error("io: {source}")]
14 Io {
15 #[from]
16 source: io::Error,
17 },
18
19 #[error("tryfromint: {source}")]
20 TryFromInt {
21 #[from]
22 source: TryFromIntError,
23 },
24
25 #[error("serde_json: {source}")]
26 Json {
27 #[from]
28 source: serde_json::Error,
29 },
30
31 #[error("reqwest: {source}")]
32 Reqwest {
33 #[from]
34 source: reqwest::Error,
35 },
36
37 #[error("chrono parse error: {source}")]
38 ChronoParse {
39 #[from]
40 source: ParseError,
41 },
42
43 #[error("config: {source}")]
44 Config {
45 #[from]
46 source: config::ConfigError,
47 },
48
49 #[error("indicatif template: {source}")]
50 IndicatifTemplate {
51 #[from]
52 source: TemplateError,
53 },
54
55 #[error("toml: {source}")]
56 TomlSer {
57 #[from]
58 source: toml::ser::Error,
59 },
60
61 #[error("toml: {source}")]
62 TomlDeser {
63 #[from]
64 source: toml::de::Error,
65 },
66
67 #[error("systemtime: {source}")]
68 Systemtime {
69 #[from]
70 source: SystemTimeError,
71 },
72
73 #[error("conversion: {source}")]
74 Conversion {
75 #[from]
76 source: ConversionError,
77 },
78
79 #[error("Request didn't return 200 OK ({0})")]
80 RequestNotOK(StatusCode),
81
82 #[error("ModError ({0})")]
83 ModError(String),
84
85 #[error("No file extension where one should be")]
86 NoFileExtension,
87
88 #[error("Desired path not found")]
89 PathNotFound,
90
91 #[error("Desired system directory {0} not found")]
92 SysDirNotFound(String),
93
94 #[error("No requested Minecraft version found")]
95 MinecraftVersionNotFound,
96
97 #[error("Mod already added")]
98 ModAlreadyOnList,
99
100 #[error("No versions are applicable for the current scope")]
101 NoCurrentVersion,
102
103 #[error("The version does not know if it's locked or not")]
104 VersionSetNotSet,
105
106 #[error("No download for the current id found")]
107 NoDownload,
108
109 #[error("No ProjectInfo found / no id given")]
110 NoProjectInfo,
111
112 #[error("The requested List was not found")]
113 ListNotFound,
114
115 #[error("No download folder given")]
116 NoDownloadFolder,
117
118 #[error("db: {source}")]
119 DB {
120 #[from]
121 source: DBError,
122 },
123}
124
125#[derive(Debug, thiserror::Error)]
126pub enum ConversionError {
127 #[error("couldn't convert to Modloader from: {0}")]
128 Modloader(String),
129
130 #[error("Path couldn't be converted to string")]
131 InvalidPath,
132}
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::{
10use tokio::task::JoinSet; 10use tokio::task::JoinSet;
11 11
12use crate::{ 12use crate::{
13 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 13 apis::modrinth::Version,
14 cache::{copy_cached_version, get_cached_versions},
15 config::Cfg,
16 data::list::List,
17 db::{mods_get_info, userlist_add_disabled_versions},
18 errors::{ConversionError, Error, MLE},
19 PROGRESS_CHARS, STYLE_BAR_BYTE, STYLE_BAR_POS, STYLE_SPINNER,
14}; 20};
15 21
16/// # Errors 22/// # Errors
@@ -25,15 +31,14 @@ pub async fn download_versions(
25 31
26 let mut js = JoinSet::new(); 32 let mut js = JoinSet::new();
27 33
28 let style_spinner = ProgressStyle::with_template(STYLE_SPINNER).map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?; 34 let style_spinner = ProgressStyle::with_template(STYLE_SPINNER)?;
29 35
30 let all = progress.insert_before( 36 let all = progress.insert_before(
31 progress_before, 37 progress_before,
32 ProgressBar::new(versions.len().try_into().map_err(|_| MLErr::new(EType::Other, "ListStackLen"))?), 38 ProgressBar::new(versions.len().try_into()?),
33 ); 39 );
34 all.set_style( 40 all.set_style(
35 ProgressStyle::with_template(STYLE_BAR_POS) 41 ProgressStyle::with_template(STYLE_BAR_POS)?
36 .map_err(|_| MLErr::new(EType::LibIndicatif, "template error"))?
37 .progress_chars(PROGRESS_CHARS), 42 .progress_chars(PROGRESS_CHARS),
38 ); 43 );
39 all.set_message(format!("✓Downloading {}", list.id)); 44 all.set_message(format!("✓Downloading {}", list.id));
@@ -87,8 +92,8 @@ async fn download_version(
87 None => files[0].clone(), 92 None => files[0].clone(),
88 }; 93 };
89 let mut splitname: Vec<&str> = file.filename.split('.').collect(); 94 let mut splitname: Vec<&str> = file.filename.split('.').collect();
90 let Ok(extension) = splitname.pop().ok_or("") else { 95 let Some(extension) = splitname.pop() else {
91 return Err(MLErr::new(EType::Other, "NO_FILE_EXTENSION")) 96 return Err(Error::NoFileExtension);
92 }; 97 };
93 let filename = format!( 98 let filename = format!(
94 "{}.mr.{}.{}.{}", 99 "{}.mr.{}.{}.{}",
@@ -169,7 +174,12 @@ pub fn disable_version(
169 174
170 rename(file, disabled)?; 175 rename(file, disabled)?;
171 176
172 userlist_add_disabled_versions(config, &current_list.id, versionid, mod_id)?; 177 userlist_add_disabled_versions(
178 config,
179 &current_list.id,
180 versionid,
181 mod_id,
182 )?;
173 183
174 Ok(()) 184 Ok(())
175} 185}
@@ -189,8 +199,8 @@ pub fn get_file_path(list: &List, versionid: &str) -> MLE<String> {
189 for file in read_dir(&list.download_folder)? { 199 for file in read_dir(&list.download_folder)? {
190 let path = file?.path(); 200 let path = file?.path();
191 if path.is_file() { 201 if path.is_file() {
192 let Ok(pathstr) = path.to_str().ok_or("") else { 202 let Some(pathstr) = path.to_str() else {
193 return Err(MLErr::new(EType::Other, "INVALID_PATH")) 203 return Err(Error::PathNotFound);
194 }; 204 };
195 let namesplit: Vec<&str> = pathstr.split('.').collect(); 205 let namesplit: Vec<&str> = pathstr.split('.').collect();
196 let ver_id = namesplit[namesplit.len() - 2]; 206 let ver_id = namesplit[namesplit.len() - 2];
@@ -198,11 +208,8 @@ pub fn get_file_path(list: &List, versionid: &str) -> MLE<String> {
198 } 208 }
199 } 209 }
200 210
201 let Ok(filename) = names.get(versionid).ok_or("") else { 211 let Some(filename) = names.get(versionid) else {
202 return Err(MLErr::new( 212 return Err(Error::PathNotFound);
203 EType::ArgumentError,
204 "VERSION_NOT_FOUND_IN_FILES",
205 ))
206 }; 213 };
207 214
208 Ok(filename.to_owned()) 215 Ok(filename.to_owned())
@@ -213,13 +220,16 @@ pub fn get_downloaded_versions(list: &List) -> MLE<HashMap<String, String>> {
213 let mut versions: HashMap<String, String> = HashMap::new(); 220 let mut versions: HashMap<String, String> = HashMap::new();
214 for file in read_dir(&list.download_folder)? { 221 for file in read_dir(&list.download_folder)? {
215 let path = file?.path(); 222 let path = file?.path();
216 if path.is_file() 223 if path.is_file() {
217 && path 224 let Some(extension) = path.extension() else {
218 .extension() 225 return Err(Error::NoFileExtension);
219 .ok_or(MLErr::new(EType::IoError, "extension"))? 226 };
220 == "jar" 227 if extension != "jar" {
221 { 228 continue;
222 let pathstr = path.to_str().ok_or(MLErr::new(EType::IoError, "path_to_str"))?; 229 }
230 let Some(pathstr) = path.to_str() else {
231 return Err(ConversionError::InvalidPath)?;
232 };
223 let namesplit: Vec<&str> = pathstr.split('.').collect(); 233 let namesplit: Vec<&str> = pathstr.split('.').collect();
224 versions.insert( 234 versions.insert(
225 String::from(namesplit[namesplit.len() - 3]), 235 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;
5pub mod commands; 5pub mod commands;
6pub mod config; 6pub mod config;
7pub mod db; 7pub mod db;
8pub mod error; 8pub mod errors;
9pub mod files; 9pub mod files;
10pub mod data; 10pub mod data;
11 11
@@ -14,7 +14,7 @@ use config::Cfg;
14use data::{gameversion::VersionLevel, list::List, modification::{AddMod, IDSelector}, modloader::Modloader}; 14use data::{gameversion::VersionLevel, list::List, modification::{AddMod, IDSelector}, modloader::Modloader};
15pub use data::{STYLE_BAR_POS, STYLE_MESSAGE, STYLE_SPINNER, STYLE_BAR_BYTE, STYLE_OPERATION, PROGRESS_CHARS}; 15pub use data::{STYLE_BAR_POS, STYLE_MESSAGE, STYLE_SPINNER, STYLE_BAR_BYTE, STYLE_OPERATION, PROGRESS_CHARS};
16use db::{config_get_current_list, lists_get, lists_get_all_ids}; 16use db::{config_get_current_list, lists_get, lists_get_all_ids};
17use error::MLE; 17use errors::MLE;
18 18
19#[derive(Parser)] 19#[derive(Parser)]
20#[command(author, version, about)] 20#[command(author, version, about)]
@@ -297,7 +297,7 @@ async fn handle_list(
297 version, 297 version,
298 } => { 298 } => {
299 let ml = match modloader { 299 let ml = match modloader {
300 Some(ml) => Modloader::from(&ml).unwrap(), 300 Some(ml) => Modloader::try_from(ml.as_str())?,
301 None => config.defaults.modloader.clone(), 301 None => config.defaults.modloader.clone(),
302 }; 302 };
303 303