summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/apis/modrinth.rs56
-rw-r--r--src/commands/download.rs38
-rw-r--r--src/commands/io.rs51
-rw-r--r--src/commands/list.rs38
-rw-r--r--src/commands/mod.rs16
-rw-r--r--src/commands/modification.rs181
-rw-r--r--src/commands/setup.rs12
-rw-r--r--src/commands/update.rs116
-rw-r--r--src/config.rs16
-rw-r--r--src/db.rs423
-rw-r--r--src/error.rs37
-rw-r--r--src/files.rs59
-rw-r--r--src/lib.rs10
-rw-r--r--src/main.rs115
14 files changed, 823 insertions, 345 deletions
diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs
index bb5ee19..9afe7f3 100644
--- a/src/apis/modrinth.rs
+++ b/src/apis/modrinth.rs
@@ -2,7 +2,10 @@ use chrono::{DateTime, FixedOffset};
2use reqwest::Client; 2use reqwest::Client;
3use serde::Deserialize; 3use serde::Deserialize;
4 4
5use crate::{Modloader, List, error::{MLE, MLError, ErrorType}}; 5use crate::{
6 error::{ErrorType, MLError, MLE},
7 List, Modloader,
8};
6 9
7#[derive(Debug, Deserialize, Clone)] 10#[derive(Debug, Deserialize, Clone)]
8pub struct Project { 11pub struct Project {
@@ -47,7 +50,7 @@ pub struct ModeratorMessage {
47pub enum Side { 50pub enum Side {
48 required, 51 required,
49 optional, 52 optional,
50 unsupported 53 unsupported,
51} 54}
52 55
53#[allow(non_camel_case_types)] 56#[allow(non_camel_case_types)]
@@ -55,7 +58,7 @@ pub enum Side {
55pub enum Type { 58pub enum Type {
56 r#mod, 59 r#mod,
57 modpack, 60 modpack,
58 recourcepack 61 recourcepack,
59} 62}
60 63
61#[allow(non_camel_case_types)] 64#[allow(non_camel_case_types)]
@@ -63,9 +66,11 @@ pub enum Type {
63pub enum Status { 66pub enum Status {
64 approved, 67 approved,
65 rejected, 68 rejected,
66 draft, unlisted, archived, 69 draft,
70 unlisted,
71 archived,
67 processing, 72 processing,
68 unknown 73 unknown,
69} 74}
70 75
71#[derive(Debug, Clone, Deserialize)] 76#[derive(Debug, Clone, Deserialize)]
@@ -90,7 +95,7 @@ pub struct Version {
90pub enum VersionType { 95pub enum VersionType {
91 release, 96 release,
92 beta, 97 beta,
93 alpha 98 alpha,
94} 99}
95 100
96#[derive(Debug, Clone, Deserialize)] 101#[derive(Debug, Clone, Deserialize)]
@@ -110,22 +115,19 @@ pub struct Hash {
110 115
111async fn get(api: &str, path: String) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> { 116async fn get(api: &str, path: String) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> {
112 let url = format!(r#"{}{}"#, api, path); 117 let url = format!(r#"{}{}"#, api, path);
113 118
114 let client = Client::builder() 119 let client = Client::builder()
115 .user_agent(format!("fxqnlr/modlistcli/{} ([email protected])", env!("CARGO_PKG_VERSION"))) 120 .user_agent(format!(
121 "fxqnlr/modlistcli/{} ([email protected])",
122 env!("CARGO_PKG_VERSION")
123 ))
116 .build()?; 124 .build()?;
117 let res = client.get(url) 125 let res = client.get(url).send().await?;
118 .send() 126
119 .await?;
120
121 let mut data: Option<Vec<u8>> = None; 127 let mut data: Option<Vec<u8>> = None;
122 128
123 if res.status() == 200 { 129 if res.status() == 200 {
124 data = Some(res 130 data = Some(res.bytes().await?.to_vec());
125 .bytes()
126 .await?
127 .to_vec()
128 );
129 } 131 }
130 132
131 Ok(data) 133 Ok(data)
@@ -143,7 +145,7 @@ pub async fn projects(api: &str, ids: Vec<String>) -> Vec<Project> {
143 let url = format!(r#"projects?ids=["{}"]"#, all); 145 let url = format!(r#"projects?ids=["{}"]"#, all);
144 146
145 let data = get(api, url).await.unwrap().unwrap(); 147 let data = get(api, url).await.unwrap().unwrap();
146 148
147 serde_json::from_slice(&data).unwrap() 149 serde_json::from_slice(&data).unwrap()
148} 150}
149 151
@@ -154,7 +156,10 @@ pub async fn versions(api: &str, id: String, list: List) -> Vec<Version> {
154 Modloader::Fabric => String::from("fabric"), 156 Modloader::Fabric => String::from("fabric"),
155 }; 157 };
156 158
157 let url = format!(r#"project/{}/version?loaders=["{}"]&game_versions=["{}"]"#, id, loaderstr, list.mc_version); 159 let url = format!(
160 r#"project/{}/version?loaders=["{}"]&game_versions=["{}"]"#,
161 id, loaderstr, list.mc_version
162 );
158 163
159 let data = get(api, url).await.unwrap(); 164 let data = get(api, url).await.unwrap();
160 165
@@ -185,7 +190,7 @@ pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
185 times.sort_by_key(|t| t.1); 190 times.sort_by_key(|t| t.1);
186 times.reverse(); 191 times.reverse();
187 Ok(times[0].0.to_string()) 192 Ok(times[0].0.to_string())
188 }, 193 }
189 _ => panic!("available_versions should never be negative"), 194 _ => panic!("available_versions should never be negative"),
190 } 195 }
191} 196}
@@ -205,16 +210,19 @@ pub struct MCVersion {
205} 210}
206 211
207pub async fn get_minecraft_version(api: &str, version: MCVersionType) -> String { 212pub async fn get_minecraft_version(api: &str, version: MCVersionType) -> String {
208 let data = get(api, String::from("tag/game_version")).await.unwrap().unwrap(); 213 let data = get(api, String::from("tag/game_version"))
214 .await
215 .unwrap()
216 .unwrap();
209 let mc_versions: Vec<MCVersion> = serde_json::from_slice(&data).unwrap(); 217 let mc_versions: Vec<MCVersion> = serde_json::from_slice(&data).unwrap();
210 let ver = match version { 218 let ver = match version {
211 MCVersionType::Release => { 219 MCVersionType::Release => {
212 let mut i = 0; 220 let mut i = 0;
213 while !mc_versions[i].major { 221 while !mc_versions[i].major {
214 i += 1; 222 i += 1;
215 }; 223 }
216 &mc_versions[i] 224 &mc_versions[i]
217 }, 225 }
218 MCVersionType::Latest => &mc_versions[0], 226 MCVersionType::Latest => &mc_versions[0],
219 MCVersionType::Specific => { 227 MCVersionType::Specific => {
220 println!("Not inplemented"); 228 println!("Not inplemented");
diff --git a/src/commands/download.rs b/src/commands/download.rs
index 4baecee..9434591 100644
--- a/src/commands/download.rs
+++ b/src/commands/download.rs
@@ -1,8 +1,14 @@
1use crate::{files::{get_downloaded_versions, download_versions, delete_version, disable_version, clean_list_dir}, db::{userlist_get_all_current_versions_with_mods, lists_get_all_ids, lists_get}, modrinth::get_raw_versions, error::{MLE, ErrorType, MLError}}; 1use crate::{config::Cfg, get_current_list, List};
2use crate::{List, get_current_list, config::Cfg}; 2use crate::{
3 db::{lists_get, lists_get_all_ids, userlist_get_all_current_versions_with_mods},
4 error::{ErrorType, MLError, MLE},
5 files::{
6 clean_list_dir, delete_version, disable_version, download_versions, get_downloaded_versions,
7 },
8 modrinth::get_raw_versions,
9};
3 10
4pub async fn download(config: Cfg, all_lists: bool, clean: bool, delete_old: bool) -> MLE<()> { 11pub async fn download(config: Cfg, all_lists: bool, clean: bool, delete_old: bool) -> MLE<()> {
5
6 let mut liststack: Vec<List> = vec![]; 12 let mut liststack: Vec<List> = vec![];
7 if all_lists { 13 if all_lists {
8 let list_ids = lists_get_all_ids(config.clone())?; 14 let list_ids = lists_get_all_ids(config.clone())?;
@@ -18,7 +24,10 @@ pub async fn download(config: Cfg, all_lists: bool, clean: bool, delete_old: boo
18 for current_list in liststack { 24 for current_list in liststack {
19 let downloaded_versions = get_downloaded_versions(current_list.clone())?; 25 let downloaded_versions = get_downloaded_versions(current_list.clone())?;
20 println!("To download: {:#?}", downloaded_versions); 26 println!("To download: {:#?}", downloaded_versions);
21 let current_version_ids = match userlist_get_all_current_versions_with_mods(config.clone(), String::from(&current_list.id)) { 27 let current_version_ids = match userlist_get_all_current_versions_with_mods(
28 config.clone(),
29 String::from(&current_list.id),
30 ) {
22 Ok(i) => Ok(i), 31 Ok(i) => Ok(i),
23 Err(e) => Err(MLError::new(ErrorType::DBError, e.to_string().as_str())), 32 Err(e) => Err(MLError::new(ErrorType::DBError, e.to_string().as_str())),
24 }?; 33 }?;
@@ -36,28 +45,37 @@ pub async fn download(config: Cfg, all_lists: bool, clean: bool, delete_old: boo
36 if current_download.is_none() || clean { 45 if current_download.is_none() || clean {
37 to_download.push(current_version); 46 to_download.push(current_version);
38 } else { 47 } else {
39 let downloaded_version = current_download.ok_or("SOMETHING_HAS_REALLY_GONE_WRONG").unwrap(); 48 let downloaded_version = current_download
49 .ok_or("SOMETHING_HAS_REALLY_GONE_WRONG")
50 .unwrap();
40 if &current_version != downloaded_version { 51 if &current_version != downloaded_version {
41 to_disable.push((mod_id.clone(), String::from(downloaded_version))); 52 to_disable.push((mod_id.clone(), String::from(downloaded_version)));
42 to_download.push(current_version); 53 to_download.push(current_version);
43 } 54 }
44 } 55 }
45 } 56 }
46 57
47 if clean { clean_list_dir(&current_list)? }; 58 if clean {
59 clean_list_dir(&current_list)?
60 };
48 61
49 if !to_download.is_empty() { 62 if !to_download.is_empty() {
50 download_versions(current_list.clone(), config.clone(), get_raw_versions(&config.apis.modrinth, to_download).await).await?; 63 download_versions(
64 current_list.clone(),
65 config.clone(),
66 get_raw_versions(&config.apis.modrinth, to_download).await,
67 )
68 .await?;
51 } else { 69 } else {
52 println!("There are no new versions to download"); 70 println!("There are no new versions to download");
53 } 71 }
54 72
55 if !to_disable.is_empty() { 73 if !to_disable.is_empty() {
56 for ver in to_disable { 74 for ver in to_disable {
57 if delete_old { 75 if delete_old {
58 println!("Deleting version {} for mod {}", ver.1, ver.0); 76 println!("Deleting version {} for mod {}", ver.1, ver.0);
59 delete_version(current_list.clone(), ver.1)?; 77 delete_version(current_list.clone(), ver.1)?;
60 } else { 78 } else {
61 disable_version(config.clone(), current_list.clone(), ver.1, ver.0)?; 79 disable_version(config.clone(), current_list.clone(), ver.1, ver.0)?;
62 }; 80 };
63 } 81 }
diff --git a/src/commands/io.rs b/src/commands/io.rs
index 5de8dd1..7f03eec 100644
--- a/src/commands/io.rs
+++ b/src/commands/io.rs
@@ -1,12 +1,18 @@
1use serde::{Deserialize, Serialize};
1use std::fs::File; 2use std::fs::File;
2use std::io::prelude::*; 3use std::io::prelude::*;
3use serde::{Serialize, Deserialize};
4 4
5use crate::{db::{lists_get, userlist_get_all_ids, lists_get_all_ids, lists_insert}, config::Cfg, Modloader, List, devdir, error::MLE, mod_add, IDSelector}; 5use crate::{
6 config::Cfg,
7 db::{lists_get, lists_get_all_ids, lists_insert, userlist_get_all_ids},
8 devdir,
9 error::MLE,
10 mod_add, IDSelector, List, Modloader,
11};
6 12
7#[derive(Debug, Serialize, Deserialize)] 13#[derive(Debug, Serialize, Deserialize)]
8struct Export { 14struct Export {
9 lists: Vec<ExportList> 15 lists: Vec<ExportList>,
10} 16}
11 17
12#[derive(Debug, Serialize, Deserialize)] 18#[derive(Debug, Serialize, Deserialize)]
@@ -20,15 +26,22 @@ struct ExportList {
20 26
21impl ExportList { 27impl ExportList {
22 pub fn from(config: Cfg, list_id: String, download: bool) -> MLE<Self> { 28 pub fn from(config: Cfg, list_id: String, download: bool) -> MLE<Self> {
23
24 let list = lists_get(config.clone(), String::from(&list_id))?; 29 let list = lists_get(config.clone(), String::from(&list_id))?;
25 30
26 let mut dl_folder = None; 31 let mut dl_folder = None;
27 if download{ dl_folder = Some(list.download_folder) }; 32 if download {
33 dl_folder = Some(list.download_folder)
34 };
28 35
29 let mods = userlist_get_all_ids(config, list_id)?.join("|"); 36 let mods = userlist_get_all_ids(config, list_id)?.join("|");
30 37
31 Ok(Self { id: list.id, mods, launcher: list.modloader.to_string(), mc_version: list.mc_version, download_folder: dl_folder }) 38 Ok(Self {
39 id: list.id,
40 mods,
41 launcher: list.modloader.to_string(),
42 mc_version: list.mc_version,
43 download_folder: dl_folder,
44 })
32 } 45 }
33} 46}
34 47
@@ -43,32 +56,44 @@ pub fn export(config: Cfg, list: Option<String>) -> MLE<()> {
43 for list_id in list_ids { 56 for list_id in list_ids {
44 lists.push(ExportList::from(config.clone(), list_id, true)?); 57 lists.push(ExportList::from(config.clone(), list_id, true)?);
45 } 58 }
46 59
47 let toml = toml::to_string( &Export { lists } )?; 60 let toml = toml::to_string(&Export { lists })?;
48 61
49 let filestr = dirs::home_dir().unwrap().join("mlexport.toml"); 62 let filestr = dirs::home_dir().unwrap().join("mlexport.toml");
50 63
51 let mut file = File::create(devdir(filestr.into_os_string().into_string().unwrap().as_str()))?; 64 let mut file = File::create(devdir(
65 filestr.into_os_string().into_string().unwrap().as_str(),
66 ))?;
52 file.write_all(toml.as_bytes())?; 67 file.write_all(toml.as_bytes())?;
53 68
54 Ok(()) 69 Ok(())
55} 70}
56 71
57pub async fn import(config: Cfg, file_str: String, direct_download: bool) -> MLE<()> { 72pub async fn import(config: Cfg, file_str: String, direct_download: bool) -> MLE<()> {
58
59 let mut file = File::open(file_str)?; 73 let mut file = File::open(file_str)?;
60 let mut content = String::new(); 74 let mut content = String::new();
61 file.read_to_string(&mut content)?; 75 file.read_to_string(&mut content)?;
62 let export: Export = toml::from_str(&content)?; 76 let export: Export = toml::from_str(&content)?;
63 77
64 for exportlist in export.lists { 78 for exportlist in export.lists {
65 let list = List { id: exportlist.id, mc_version: exportlist.mc_version, modloader: Modloader::from(&exportlist.launcher)?, download_folder: exportlist.download_folder.ok_or("NO_DL").unwrap() }; 79 let list = List {
66 lists_insert(config.clone(), list.id.clone(), list.mc_version.clone(), list.modloader.clone(), String::from(&list.download_folder))?; 80 id: exportlist.id,
81 mc_version: exportlist.mc_version,
82 modloader: Modloader::from(&exportlist.launcher)?,
83 download_folder: exportlist.download_folder.ok_or("NO_DL").unwrap(),
84 };
85 lists_insert(
86 config.clone(),
87 list.id.clone(),
88 list.mc_version.clone(),
89 list.modloader.clone(),
90 String::from(&list.download_folder),
91 )?;
67 let mods: Vec<&str> = exportlist.mods.split('|').collect(); 92 let mods: Vec<&str> = exportlist.mods.split('|').collect();
68 let mut mod_ids = vec![]; 93 let mut mod_ids = vec![];
69 for mod_id in mods { 94 for mod_id in mods {
70 mod_ids.push(IDSelector::ModificationID(String::from(mod_id))); 95 mod_ids.push(IDSelector::ModificationID(String::from(mod_id)));
71 }; 96 }
72 //TODO impl set_version and good direct download 97 //TODO impl set_version and good direct download
73 //TODO impl all at once, dafuck 98 //TODO impl all at once, dafuck
74 mod_add(config.clone(), mod_ids, list, direct_download, false).await?; 99 mod_add(config.clone(), mod_ids, list, direct_download, false).await?;
diff --git a/src/commands/list.rs b/src/commands/list.rs
index 80e801a..13176f4 100644
--- a/src/commands/list.rs
+++ b/src/commands/list.rs
@@ -1,4 +1,12 @@
1use crate::{db::{lists_insert, lists_remove, config_change_current_list, config_get_current_list, lists_get, lists_version}, Modloader, config::Cfg, update, error::MLE}; 1use crate::{
2 config::Cfg,
3 db::{
4 config_change_current_list, config_get_current_list, lists_get, lists_insert, lists_remove,
5 lists_version,
6 },
7 error::MLE,
8 update, Modloader,
9};
2 10
3#[derive(Debug, Clone, PartialEq, Eq)] 11#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct List { 12pub struct List {
@@ -13,7 +21,13 @@ pub fn get_current_list(config: Cfg) -> MLE<List> {
13 lists_get(config, id) 21 lists_get(config, id)
14} 22}
15 23
16pub fn list_add(config: Cfg, id: String, mc_version: String, modloader: Modloader, directory: String) -> MLE<()> { 24pub fn list_add(
25 config: Cfg,
26 id: String,
27 mc_version: String,
28 modloader: Modloader,
29 directory: String,
30) -> MLE<()> {
17 lists_insert(config, id, mc_version, modloader, directory) 31 lists_insert(config, id, mc_version, modloader, directory)
18} 32}
19 33
@@ -30,15 +44,27 @@ pub fn list_remove(config: Cfg, id: String) -> MLE<()> {
30///Changing the current lists version and updating it 44///Changing the current lists version and updating it
31/// 45///
32/// #Arguments 46/// #Arguments
33/// 47///
34/// * `config` - The current config 48/// * `config` - The current config
35/// * `args` - All args, to extract the new version 49/// * `args` - All args, to extract the new version
36pub async fn list_version(config: Cfg, id: String, mc_version: String, download: bool, delete: bool) -> MLE<()> { 50pub async fn list_version(
37 println!("Change version for list {} to minecraft version: {}", id, mc_version); 51 config: Cfg,
52 id: String,
53 mc_version: String,
54 download: bool,
55 delete: bool,
56) -> MLE<()> {
57 println!(
58 "Change version for list {} to minecraft version: {}",
59 id, mc_version
60 );
38 61
39 lists_version(config.clone(), &id, &mc_version)?; 62 lists_version(config.clone(), &id, &mc_version)?;
40 63
41 println!("\nCheck for updates for new minecraft version in list {}", id); 64 println!(
65 "\nCheck for updates for new minecraft version in list {}",
66 id
67 );
42 let list = lists_get(config.clone(), id)?; 68 let list = lists_get(config.clone(), id)?;
43 update(config, vec![list], true, download, delete).await 69 update(config, vec![list], true, download, delete).await
44} 70}
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 0d5bd00..1c7c012 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -1,13 +1,13 @@
1pub mod modification;
2pub mod list;
3pub mod update;
4pub mod setup;
5pub mod download; 1pub mod download;
6pub mod io; 2pub mod io;
3pub mod list;
4pub mod modification;
5pub mod setup;
6pub mod update;
7 7
8pub use modification::*;
9pub use list::*;
10pub use update::*;
11pub use setup::*;
12pub use download::*; 8pub use download::*;
13pub use io::*; 9pub use io::*;
10pub use list::*;
11pub use modification::*;
12pub use setup::*;
13pub use update::*;
diff --git a/src/commands/modification.rs b/src/commands/modification.rs
index 454e148..ffc4e10 100644
--- a/src/commands/modification.rs
+++ b/src/commands/modification.rs
@@ -1,9 +1,19 @@
1use crate::{modrinth::{versions, extract_current_version, Version, projects, get_raw_versions, project}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, userlist_get_all_ids, userlist_get_current_version, lists_get_all_ids, mods_remove}, files::{delete_version, download_versions}, List, error::{MLE, ErrorType, MLError}}; 1use crate::{
2 config::Cfg,
3 db::{
4 lists_get_all_ids, mods_get_id, mods_insert, mods_remove, userlist_get_all_ids,
5 userlist_get_current_version, userlist_insert, userlist_remove,
6 },
7 error::{ErrorType, MLError, MLE},
8 files::{delete_version, download_versions},
9 modrinth::{extract_current_version, get_raw_versions, project, projects, versions, Version},
10 List,
11};
2 12
3#[derive(Debug, Clone, PartialEq, Eq)] 13#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum IDSelector { 14pub enum IDSelector {
5 ModificationID(String), 15 ModificationID(String),
6 VersionID(String) 16 VersionID(String),
7} 17}
8 18
9#[derive(Debug, Clone)] 19#[derive(Debug, Clone)]
@@ -16,10 +26,16 @@ pub struct ProjectInfo {
16 pub download_link: String, 26 pub download_link: String,
17} 27}
18 28
19pub async fn mod_add(config: Cfg, ids: Vec<IDSelector>, list: List, direct_download: bool, set_version: bool) -> MLE<()> { 29pub async fn mod_add(
30 config: Cfg,
31 ids: Vec<IDSelector>,
32 list: List,
33 direct_download: bool,
34 set_version: bool,
35) -> MLE<()> {
20 println!("Add mods to {}", list.id); 36 println!("Add mods to {}", list.id);
21 println!(" └Add mods:"); 37 println!(" └Add mods:");
22 38
23 let mut mod_ids: Vec<String> = Vec::new(); 39 let mut mod_ids: Vec<String> = Vec::new();
24 let mut ver_ids: Vec<String> = Vec::new(); 40 let mut ver_ids: Vec<String> = Vec::new();
25 41
@@ -32,11 +48,17 @@ pub async fn mod_add(config: Cfg, ids: Vec<IDSelector>, list: List, direct_downl
32 } 48 }
33 49
34 let mut projectinfo: Vec<ProjectInfo> = Vec::new(); 50 let mut projectinfo: Vec<ProjectInfo> = Vec::new();
35 if !mod_ids.is_empty() { projectinfo.append(&mut get_mod_infos(config.clone(), mod_ids, list.clone()).await?) }; 51 if !mod_ids.is_empty() {
36 if !ver_ids.is_empty() { projectinfo.append(&mut get_ver_info(config.clone(), ver_ids).await?) }; 52 projectinfo.append(&mut get_mod_infos(config.clone(), mod_ids, list.clone()).await?)
53 };
54 if !ver_ids.is_empty() {
55 projectinfo.append(&mut get_ver_info(config.clone(), ver_ids).await?)
56 };
57
58 if projectinfo.is_empty() {
59 return Err(MLError::new(ErrorType::ArgumentError, "NO_IDS?"));
60 };
37 61
38 if projectinfo.is_empty() { return Err(MLError::new(ErrorType::ArgumentError, "NO_IDS?")) };
39
40 let mut downloadstack: Vec<Version> = Vec::new(); 62 let mut downloadstack: Vec<Version> = Vec::new();
41 63
42 //Adding each mod to the lists and downloadstack 64 //Adding each mod to the lists and downloadstack
@@ -45,29 +67,59 @@ pub async fn mod_add(config: Cfg, ids: Vec<IDSelector>, list: List, direct_downl
45 } else { 67 } else {
46 println!(" └Insert mods in list {} and save infos", list.id); 68 println!(" └Insert mods in list {} and save infos", list.id);
47 } 69 }
48 70
49 for project in projectinfo { 71 for project in projectinfo {
50 let current_version_id = if project.current_version.is_none() { String::from("NONE") } else { project.current_version.clone().unwrap().id }; 72 let current_version_id = if project.current_version.is_none() {
51 match userlist_insert(config.clone(), &list.id, &project.mod_id, &current_version_id, project.clone().applicable_versions, &project.download_link, set_version) { 73 String::from("NONE")
74 } else {
75 project.current_version.clone().unwrap().id
76 };
77 match userlist_insert(
78 config.clone(),
79 &list.id,
80 &project.mod_id,
81 &current_version_id,
82 project.clone().applicable_versions,
83 &project.download_link,
84 set_version,
85 ) {
52 Err(e) => { 86 Err(e) => {
53 let expected_err = format!("SQL: UNIQUE constraint failed: {}.mod_id", list.id); 87 let expected_err = format!("SQL: UNIQUE constraint failed: {}.mod_id", list.id);
54 if e.to_string() == expected_err { Err(MLError::new(ErrorType::ModError, "MOD_ALREADY_ON_SELECTED_LIST")) } else { Err(e) } 88 if e.to_string() == expected_err {
55 }, 89 Err(MLError::new(
56 Ok(..) => { Ok(..) }, 90 ErrorType::ModError,
91 "MOD_ALREADY_ON_SELECTED_LIST",
92 ))
93 } else {
94 Err(e)
95 }
96 }
97 Ok(..) => Ok(..),
57 }?; 98 }?;
58 99
59 match mods_insert(config.clone(), &project.mod_id, &project.slug, &project.title) { 100 match mods_insert(
101 config.clone(),
102 &project.mod_id,
103 &project.slug,
104 &project.title,
105 ) {
60 Err(e) => { 106 Err(e) => {
61 if e.to_string() == "SQL: UNIQUE constraint failed: mods.id" { Ok(..) } else { Err(e) } 107 if e.to_string() == "SQL: UNIQUE constraint failed: mods.id" {
62 }, 108 Ok(..)
109 } else {
110 Err(e)
111 }
112 }
63 Ok(..) => Ok(..), 113 Ok(..) => Ok(..),
64 }?; 114 }?;
65 115
66 if project.current_version.is_some() { downloadstack.push(project.current_version.unwrap()) }; 116 if project.current_version.is_some() {
117 downloadstack.push(project.current_version.unwrap())
118 };
67 } 119 }
68 120
69 //Download all the added mods 121 //Download all the added mods
70 if direct_download { 122 if direct_download {
71 download_versions(list.clone(), config.clone(), downloadstack).await?; 123 download_versions(list.clone(), config.clone(), downloadstack).await?;
72 }; 124 };
73 125
@@ -86,7 +138,12 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<String>, list: List) -> MLE<Vec
86 for project in m_projects { 138 for project in m_projects {
87 println!("\t└{}", project.title); 139 println!("\t└{}", project.title);
88 println!("\t └Get versions"); 140 println!("\t └Get versions");
89 let available_versions = versions(&config.apis.modrinth, String::from(&project.id), list.clone()).await; 141 let available_versions = versions(
142 &config.apis.modrinth,
143 String::from(&project.id),
144 list.clone(),
145 )
146 .await;
90 147
91 let mut available_versions_vec: Vec<String> = Vec::new(); 148 let mut available_versions_vec: Vec<String> = Vec::new();
92 let current_version: Option<Version>; 149 let current_version: Option<Version>;
@@ -95,36 +152,63 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<String>, list: List) -> MLE<Vec
95 let current_id = extract_current_version(available_versions.clone())?; 152 let current_id = extract_current_version(available_versions.clone())?;
96 println!("\t └Current version: {}", current_id); 153 println!("\t └Current version: {}", current_id);
97 154
98 current_version = Some(available_versions.clone().into_iter().find(|v| v.id == current_id).unwrap()); 155 current_version = Some(
156 available_versions
157 .clone()
158 .into_iter()
159 .find(|v| v.id == current_id)
160 .unwrap(),
161 );
99 162
100 file = current_version.clone().ok_or("").unwrap().files.into_iter().find(|f| f.primary).unwrap().url; 163 file = current_version
164 .clone()
165 .ok_or("")
166 .unwrap()
167 .files
168 .into_iter()
169 .find(|f| f.primary)
170 .unwrap()
171 .url;
101 for ver in available_versions { 172 for ver in available_versions {
102 available_versions_vec.push(ver.id); 173 available_versions_vec.push(ver.id);
103 }; 174 }
104
105 projectinfo.push(ProjectInfo { mod_id: project.id, slug: project.slug, title: project.title, current_version, applicable_versions: available_versions_vec, download_link: file })
106 175
176 projectinfo.push(ProjectInfo {
177 mod_id: project.id,
178 slug: project.slug,
179 title: project.title,
180 current_version,
181 applicable_versions: available_versions_vec,
182 download_link: file,
183 })
107 } else { 184 } else {
108 println!("\t └There's currently no mod version for your specified target"); 185 println!("\t └There's currently no mod version for your specified target");
109 current_version = None; 186 current_version = None;
110 file = String::from("NONE"); 187 file = String::from("NONE");
111 available_versions_vec.push(String::from("NONE")); 188 available_versions_vec.push(String::from("NONE"));
112 projectinfo.push(ProjectInfo { mod_id: project.id, slug: project.slug, title: project.title, current_version, applicable_versions: available_versions_vec, download_link: file }) 189 projectinfo.push(ProjectInfo {
190 mod_id: project.id,
191 slug: project.slug,
192 title: project.title,
193 current_version,
194 applicable_versions: available_versions_vec,
195 download_link: file,
196 })
113 } 197 }
114 }; 198 }
115 199
116 Ok(projectinfo) 200 Ok(projectinfo)
117} 201}
118 202
119async fn get_ver_info(config: Cfg, ver_ids: Vec<String>) -> MLE<Vec<ProjectInfo>> { 203async fn get_ver_info(config: Cfg, ver_ids: Vec<String>) -> MLE<Vec<ProjectInfo>> {
120 let mut projectinfo: Vec<ProjectInfo> = Vec::new(); 204 let mut projectinfo: Vec<ProjectInfo> = Vec::new();
121 205
122 //Get required information from ver_ids 206 //Get required information from ver_ids
123 let mut v_versions = get_raw_versions(&config.apis.modrinth, ver_ids).await; 207 let mut v_versions = get_raw_versions(&config.apis.modrinth, ver_ids).await;
124 let mut v_mod_ids: Vec<String> = Vec::new(); 208 let mut v_mod_ids: Vec<String> = Vec::new();
125 for ver in v_versions.clone() { 209 for ver in v_versions.clone() {
126 v_mod_ids.push(ver.project_id); 210 v_mod_ids.push(ver.project_id);
127 }; 211 }
128 let mut v_projects = projects(&config.apis.modrinth, v_mod_ids).await; 212 let mut v_projects = projects(&config.apis.modrinth, v_mod_ids).await;
129 v_versions.sort_by(|a, b| a.project_id.cmp(&b.project_id)); 213 v_versions.sort_by(|a, b| a.project_id.cmp(&b.project_id));
130 v_projects.sort_by(|a, b| a.id.cmp(&b.id)); 214 v_projects.sort_by(|a, b| a.id.cmp(&b.id));
@@ -132,9 +216,22 @@ async fn get_ver_info(config: Cfg, ver_ids: Vec<String>) -> MLE<Vec<ProjectInfo>
132 for (i, project) in v_projects.into_iter().enumerate() { 216 for (i, project) in v_projects.into_iter().enumerate() {
133 let version = &v_versions[i]; 217 let version = &v_versions[i];
134 println!("\t└{}({})", project.title, version.id); 218 println!("\t└{}({})", project.title, version.id);
135 let file = version.clone().files.into_iter().find(|f| f.primary).unwrap().url; 219 let file = version
136 projectinfo.push(ProjectInfo { mod_id: project.id, slug: project.slug, title: project.title, current_version: Some(version.clone()), applicable_versions: vec![String::from(&version.id)], download_link: file }) 220 .clone()
137 }; 221 .files
222 .into_iter()
223 .find(|f| f.primary)
224 .unwrap()
225 .url;
226 projectinfo.push(ProjectInfo {
227 mod_id: project.id,
228 slug: project.slug,
229 title: project.title,
230 current_version: Some(version.clone()),
231 applicable_versions: vec![String::from(&version.id)],
232 download_link: file,
233 })
234 }
138 Ok(projectinfo) 235 Ok(projectinfo)
139} 236}
140 237
@@ -145,24 +242,28 @@ async fn get_ver_info(config: Cfg, ver_ids: Vec<String>) -> MLE<Vec<ProjectInfo>
145/// * `id` - name, slug or id of the mod 242/// * `id` - name, slug or id of the mod
146/// * `list` - List struct 243/// * `list` - List struct
147pub fn mod_remove(config: Cfg, id: &str, list: List) -> MLE<()> { 244pub fn mod_remove(config: Cfg, id: &str, list: List) -> MLE<()> {
148
149 let mod_id = mods_get_id(&config.data, id)?; 245 let mod_id = mods_get_id(&config.data, id)?;
150 246
151 let version = userlist_get_current_version(config.clone(), &list.id, &mod_id)?; 247 let version = userlist_get_current_version(config.clone(), &list.id, &mod_id)?;
152 248
153 userlist_remove(config.clone(), &list.id, &mod_id)?; 249 userlist_remove(config.clone(), &list.id, &mod_id)?;
154 delete_version(list, version)?; 250 delete_version(list, version)?;
155 251
156 let list_ids = lists_get_all_ids(config.clone())?; 252 let list_ids = lists_get_all_ids(config.clone())?;
157 253
158 // Remove mod from main list if not used elsewhere 254 // Remove mod from main list if not used elsewhere
159 let mut mod_used = false; 255 let mut mod_used = false;
160 for id in list_ids { 256 for id in list_ids {
161 let mods = userlist_get_all_ids(config.clone(), id)?; 257 let mods = userlist_get_all_ids(config.clone(), id)?;
162 if mods.contains(&mod_id) { mod_used = true; break; }; 258 if mods.contains(&mod_id) {
163 }; 259 mod_used = true;
260 break;
261 };
262 }
164 263
165 if !mod_used { mods_remove(config, mod_id)?; }; 264 if !mod_used {
265 mods_remove(config, mod_id)?;
266 };
166 267
167 Ok(()) 268 Ok(())
168} 269}
diff --git a/src/commands/setup.rs b/src/commands/setup.rs
index 0161bd7..40e8c0a 100644
--- a/src/commands/setup.rs
+++ b/src/commands/setup.rs
@@ -1,14 +1,14 @@
1use std::{fs::File, path::Path}; 1use std::{fs::File, path::Path};
2 2
3use crate::{config::Cfg, db::db_setup, error::MLE, devdir}; 3use crate::{config::Cfg, db::db_setup, devdir, error::MLE};
4 4
5pub async fn setup(config: Cfg) -> MLE<()> { 5pub async fn setup(config: Cfg) -> MLE<()> {
6 let db_file = devdir(format!("{}/data.db", config.data).as_str()); 6 let db_file = devdir(format!("{}/data.db", config.data).as_str());
7 7
8 if !Path::new(&db_file).exists() { 8 if !Path::new(&db_file).exists() {
9 create(config, db_file)?; 9 create(config, db_file)?;
10 } 10 }
11 11
12 /* 12 /*
13 match s_config_get_version(config.clone()) { 13 match s_config_get_version(config.clone()) {
14 Ok(ver) => { 14 Ok(ver) => {
@@ -21,12 +21,11 @@ pub async fn setup(config: Cfg) -> MLE<()> {
21 Err(..) => to_02(config).await? 21 Err(..) => to_02(config).await?
22 }; 22 };
23 */ 23 */
24 24
25 Ok(()) 25 Ok(())
26} 26}
27 27
28fn create(config: Cfg, db_file: String) -> MLE<()> { 28fn create(config: Cfg, db_file: String) -> MLE<()> {
29
30 println!("Create database"); 29 println!("Create database");
31 30
32 File::create(db_file)?; 31 File::create(db_file)?;
@@ -44,7 +43,7 @@ fn create(config: Cfg, db_file: String) -> MLE<()> {
44// let full_list = lists_get(config.clone(), String::from(&list))?; 43// let full_list = lists_get(config.clone(), String::from(&list))?;
45// 44//
46// let versions = userlist_get_all_current_version_ids(config.clone(), full_list.clone().id)?; 45// let versions = userlist_get_all_current_version_ids(config.clone(), full_list.clone().id)?;
47// 46//
48// let raw_versions = get_raw_versions(String::from(&config.apis.modrinth), versions).await; 47// let raw_versions = get_raw_versions(String::from(&config.apis.modrinth), versions).await;
49// 48//
50// for ver in raw_versions { 49// for ver in raw_versions {
@@ -69,4 +68,3 @@ fn create(config: Cfg, db_file: String) -> MLE<()> {
69// } 68// }
70// s_config_update_version(config, String::from("0.4")) 69// s_config_update_version(config, String::from("0.4"))
71//} 70//}
72
diff --git a/src/commands/update.rs b/src/commands/update.rs
index e5751c0..3d9578b 100644
--- a/src/commands/update.rs
+++ b/src/commands/update.rs
@@ -1,14 +1,30 @@
1use crate::{config::Cfg, modrinth::{versions, extract_current_version, Version}, db::{userlist_get_all_ids, userlist_get_applicable_versions, userlist_change_versions, userlist_get_current_version, userlist_get_set_version, mods_get_info}, List, files::{delete_version, download_versions, disable_version, clean_list_dir}, error::{MLE, MLError, ErrorType}}; 1use crate::{
2 2 config::Cfg,
3pub async fn update(config: Cfg, liststack: Vec<List>, clean: bool, direct_download: bool, delete_old: bool) -> MLE<()> { 3 db::{
4 mods_get_info, userlist_change_versions, userlist_get_all_ids,
5 userlist_get_applicable_versions, userlist_get_current_version, userlist_get_set_version,
6 },
7 error::{ErrorType, MLError, MLE},
8 files::{clean_list_dir, delete_version, disable_version, download_versions},
9 modrinth::{extract_current_version, versions, Version},
10 List,
11};
12
13pub async fn update(
14 config: Cfg,
15 liststack: Vec<List>,
16 clean: bool,
17 direct_download: bool,
18 delete_old: bool,
19) -> MLE<()> {
4 for current_list in liststack { 20 for current_list in liststack {
5 let mods = userlist_get_all_ids(config.clone(), current_list.clone().id)?; 21 let mods = userlist_get_all_ids(config.clone(), current_list.clone().id)?;
6 22
7 let mut current_versions: Vec<(String, String)> = vec![]; 23 let mut current_versions: Vec<(String, String)> = vec![];
8 24
9 println!(" └Update mods:"); 25 println!(" └Update mods:");
10 let mut updatestack: Vec<Version> = vec![]; 26 let mut updatestack: Vec<Version> = vec![];
11 27
12 for id in mods { 28 for id in mods {
13 let info = mods_get_info(config.clone(), &id)?; 29 let info = mods_get_info(config.clone(), &id)?;
14 println!("\t└{}", info.title); 30 println!("\t└{}", info.title);
@@ -19,27 +35,39 @@ pub async fn update(config: Cfg, liststack: Vec<List>, clean: bool, direct_downl
19 } 35 }
20 36
21 //Getting current installed version for disable or delete 37 //Getting current installed version for disable or delete
22 let disable_version = userlist_get_current_version(config.clone(), &current_list.id, &id)?; 38 let disable_version =
39 userlist_get_current_version(config.clone(), &current_list.id, &id)?;
23 40
24 updatestack.push( 41 updatestack.push(
25 match specific_update(config.clone(), clean, current_list.clone(), String::from(&id)).await { 42 match specific_update(
43 config.clone(),
44 clean,
45 current_list.clone(),
46 String::from(&id),
47 )
48 .await
49 {
26 Ok(ver) => { 50 Ok(ver) => {
27 current_versions.push((disable_version, id)); 51 current_versions.push((disable_version, id));
28 ver 52 ver
29 }, 53 }
30 Err(e) => { 54 Err(e) => {
31 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" { 55 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" {
32 println!("\t └No new version found for the specified minecraft version"); 56 println!(
57 "\t └No new version found for the specified minecraft version"
58 );
33 } else { 59 } else {
34 return Err(e); 60 return Err(e);
35 }; 61 };
36 continue; 62 continue;
37 } 63 }
38 } 64 },
39 ) 65 )
40 }; 66 }
41 67
42 if clean { clean_list_dir(&current_list)?; }; 68 if clean {
69 clean_list_dir(&current_list)?;
70 };
43 71
44 if direct_download && !updatestack.is_empty() { 72 if direct_download && !updatestack.is_empty() {
45 download_versions(current_list.clone(), config.clone(), updatestack).await?; 73 download_versions(current_list.clone(), config.clone(), updatestack).await?;
@@ -50,7 +78,7 @@ pub async fn update(config: Cfg, liststack: Vec<List>, clean: bool, direct_downl
50 if delete_old { 78 if delete_old {
51 println!("\t └Delete version {}", ver.0); 79 println!("\t └Delete version {}", ver.0);
52 delete_version(current_list.clone(), ver.0)?; 80 delete_version(current_list.clone(), ver.0)?;
53 } else if ver.0 != "NONE" { 81 } else if ver.0 != "NONE" {
54 println!("\t └Disable version {}", ver.0); 82 println!("\t └Disable version {}", ver.0);
55 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?; 83 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?;
56 }; 84 };
@@ -63,10 +91,11 @@ pub async fn update(config: Cfg, liststack: Vec<List>, clean: bool, direct_downl
63} 91}
64 92
65async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> MLE<Version> { 93async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> MLE<Version> {
66 let applicable_versions = versions(&config.apis.modrinth, String::from(&id), list.clone()).await; 94 let applicable_versions =
67 95 versions(&config.apis.modrinth, String::from(&id), list.clone()).await;
96
68 let mut versions: Vec<String> = vec![]; 97 let mut versions: Vec<String> = vec![];
69 98
70 if !applicable_versions.is_empty() { 99 if !applicable_versions.is_empty() {
71 for ver in &applicable_versions { 100 for ver in &applicable_versions {
72 versions.push(String::from(&ver.id)); 101 versions.push(String::from(&ver.id));
@@ -77,8 +106,14 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML
77 106
78 let mut current: Vec<Version> = vec![]; 107 let mut current: Vec<Version> = vec![];
79 //TODO Split clean and no match 108 //TODO Split clean and no match
80 if clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&id))?) { 109 if clean
81 110 || (versions.join("|")
111 != userlist_get_applicable_versions(
112 config.clone(),
113 String::from(&list.id),
114 String::from(&id),
115 )?)
116 {
82 let current_str = extract_current_version(applicable_versions.clone())?; 117 let current_str = extract_current_version(applicable_versions.clone())?;
83 118
84 if clean { 119 if clean {
@@ -89,35 +124,54 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML
89 }; 124 };
90 125
91 //get new versions 126 //get new versions
92 let current_ver = match applicable_versions.into_iter().find(|ver| ver.id == current_str).ok_or("!no current version in applicable_versions") { 127 let current_ver = match applicable_versions
128 .into_iter()
129 .find(|ver| ver.id == current_str)
130 .ok_or("!no current version in applicable_versions")
131 {
93 Ok(v) => Ok(v), 132 Ok(v) => Ok(v),
94 Err(e) => Err(MLError::new(ErrorType::Other, e)), 133 Err(e) => Err(MLError::new(ErrorType::Other, e)),
95 }?; 134 }?;
96 current.push(current_ver.clone()); 135 current.push(current_ver.clone());
97 136
98 //TODO implement version selection if no primary 137 //TODO implement version selection if no primary
99 let link = match current_ver.files.into_iter().find(|f| f.primary).ok_or("!no primary in links") { 138 let link = match current_ver
139 .files
140 .into_iter()
141 .find(|f| f.primary)
142 .ok_or("!no primary in links")
143 {
100 Ok(p) => Ok(p), 144 Ok(p) => Ok(p),
101 Err(e) => Err(MLError::new(ErrorType::Other, e)), 145 Err(e) => Err(MLError::new(ErrorType::Other, e)),
102 }?.url; 146 }?
147 .url;
103 userlist_change_versions(config, list.id, current_str, versions.join("|"), link, id)?; 148 userlist_change_versions(config, list.id, current_str, versions.join("|"), link, id)?;
104 } 149 }
105 150
106 if current.is_empty() { return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE")) }; 151 if current.is_empty() {
107 152 return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE"));
153 };
154
108 //println!(" └✔️"); 155 //println!(" └✔️");
109 Ok(current[0].clone()) 156 Ok(current[0].clone())
110} 157}
111 158
112#[tokio::test] 159#[tokio::test]
113async fn download_updates_test() { 160async fn download_updates_test() {
161 use crate::{
162 modrinth::{Hash, Version, VersionFile, VersionType},
163 List, Modloader,
164 };
114 165
115 use crate::{modrinth::{Version, VersionFile, Hash, VersionType}, Modloader, List};
116
117 let config = Cfg::init("modlist.toml").unwrap(); 166 let config = Cfg::init("modlist.toml").unwrap();
118 let current_list = List { id: String::from("..."), mc_version: String::from("..."), modloader: Modloader::Fabric, download_folder: String::from("./dev/tests/dl") }; 167 let current_list = List {
119 168 id: String::from("..."),
120 let versions = vec![Version { 169 mc_version: String::from("..."),
170 modloader: Modloader::Fabric,
171 download_folder: String::from("./dev/tests/dl"),
172 };
173
174 let versions = vec![Version {
121 id: "dEqtGnT9".to_string(), 175 id: "dEqtGnT9".to_string(),
122 project_id: "kYuIpRLv".to_string(), 176 project_id: "kYuIpRLv".to_string(),
123 author_id: "Qnt13hO8".to_string(), 177 author_id: "Qnt13hO8".to_string(),
@@ -147,5 +201,7 @@ async fn download_updates_test() {
147 "fabric".to_string() 201 "fabric".to_string()
148 ] 202 ]
149 }]; 203 }];
150 assert!(download_versions(current_list, config, versions).await.is_ok()) 204 assert!(download_versions(current_list, config, versions)
205 .await
206 .is_ok())
151} 207}
diff --git a/src/config.rs b/src/config.rs
index 075d884..1b54d5f 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,8 +1,11 @@
1use std::{fs::File, io::{Read, Write}}; 1use std::{
2 fs::File,
3 io::{Read, Write},
4};
2 5
3use serde::{Serialize, Deserialize}; 6use serde::{Deserialize, Serialize};
4 7
5use crate::{error::MLE, devdir}; 8use crate::{devdir, error::MLE};
6 9
7#[derive(Debug, Clone, Serialize, Deserialize)] 10#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Cfg { 11pub struct Cfg {
@@ -24,7 +27,12 @@ impl Cfg {
24 Err(err) => { 27 Err(err) => {
25 if err.kind() == std::io::ErrorKind::NotFound { 28 if err.kind() == std::io::ErrorKind::NotFound {
26 println!("No config file found, creating one"); 29 println!("No config file found, creating one");
27 let default_cfg = Cfg { data: String::from("./"), apis: Apis { modrinth: String::from("https://api.modrinth.com/v2/") } }; 30 let default_cfg = Cfg {
31 data: String::from("./"),
32 apis: Apis {
33 modrinth: String::from("https://api.modrinth.com/v2/"),
34 },
35 };
28 let mut file = File::create(devdir(configfile.to_str().unwrap()))?; 36 let mut file = File::create(devdir(configfile.to_str().unwrap()))?;
29 println!("Created config file"); 37 println!("Created config file");
30 file.write_all(toml::to_string(&default_cfg)?.as_bytes())?; 38 file.write_all(toml::to_string(&default_cfg)?.as_bytes())?;
diff --git a/src/db.rs b/src/db.rs
index 2c48cab..09d54c2 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -2,17 +2,21 @@ use std::io::{Error, ErrorKind};
2 2
3use rusqlite::Connection; 3use rusqlite::Connection;
4 4
5use crate::{Modloader, config::Cfg, List, devdir, error::{MLE, MLError, ErrorType}}; 5use crate::{
6 config::Cfg,
7 devdir,
8 error::{ErrorType, MLError, MLE},
9 List, Modloader,
10};
6 11
7//MODS 12//MODS
8pub fn mods_insert(config: Cfg, id: &str, slug: &str, name: &str) -> MLE<()> { 13pub fn mods_insert(config: Cfg, id: &str, slug: &str, name: &str) -> MLE<()> {
9
10 let data = devdir(format!("{}/data.db", config.data).as_str()); 14 let data = devdir(format!("{}/data.db", config.data).as_str());
11 let connection = Connection::open(data)?; 15 let connection = Connection::open(data)?;
12 16
13 connection.execute( 17 connection.execute(
14 "INSERT INTO mods (id, slug, title) VALUES (?1, ?2, ?3)", 18 "INSERT INTO mods (id, slug, title) VALUES (?1, ?2, ?3)",
15 [id, slug, name.replace('\'', "").as_str()] 19 [id, slug, name.replace('\'', "").as_str()],
16 )?; 20 )?;
17 21
18 Ok(()) 22 Ok(())
@@ -21,13 +25,11 @@ pub fn mods_insert(config: Cfg, id: &str, slug: &str, name: &str) -> MLE<()> {
21pub fn mods_get_all_ids(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::Error>> { 25pub fn mods_get_all_ids(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::Error>> {
22 let data = devdir(format!("{}/data.db", config.data).as_str()); 26 let data = devdir(format!("{}/data.db", config.data).as_str());
23 let connection = Connection::open(data).unwrap(); 27 let connection = Connection::open(data).unwrap();
24 28
25 let mut mods: Vec<String> = Vec::new(); 29 let mut mods: Vec<String> = Vec::new();
26 30
27 let mut stmt = connection.prepare("SELECT id FROM mods")?; 31 let mut stmt = connection.prepare("SELECT id FROM mods")?;
28 let id_iter = stmt.query_map([], |row| { 32 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
29 row.get::<usize, String>(0)
30 })?;
31 33
32 for id in id_iter { 34 for id in id_iter {
33 mods.push(id?); 35 mods.push(id?);
@@ -49,36 +51,33 @@ pub fn mods_get_all_ids(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::
49/// 51///
50///Will return `MLError` when no mod id is found 52///Will return `MLError` when no mod id is found
51pub fn mods_get_id(data: &str, slug: &str) -> MLE<String> { 53pub fn mods_get_id(data: &str, slug: &str) -> MLE<String> {
52
53 //TODO check if "slug" is id 54 //TODO check if "slug" is id
54 55
55 let data = devdir(format!("{}/data.db", data).as_str()); 56 let data = devdir(format!("{}/data.db", data).as_str());
56 let connection = Connection::open(data)?; 57 let connection = Connection::open(data)?;
57 58
58 let mut mod_id = String::new(); 59 let mut mod_id = String::new();
59 60
60 //get from slug 61 //get from slug
61 let mut stmt = connection.prepare("SELECT id FROM mods WHERE slug = ?")?; 62 let mut stmt = connection.prepare("SELECT id FROM mods WHERE slug = ?")?;
62 let id_iter = stmt.query_map([slug], |row| { 63 let id_iter = stmt.query_map([slug], |row| row.get::<usize, String>(0))?;
63 row.get::<usize, String>(0)
64 })?;
65 64
66 for id in id_iter { 65 for id in id_iter {
67 mod_id = id?; 66 mod_id = id?;
68 }; 67 }
69 //get from title if no id found from slug 68 //get from title if no id found from slug
70 if mod_id.is_empty() { 69 if mod_id.is_empty() {
71 let mut stmt = connection.prepare("SELECT id FROM mods WHERE title = ?")?; 70 let mut stmt = connection.prepare("SELECT id FROM mods WHERE title = ?")?;
72 let id_iter = stmt.query_map([slug], |row| { 71 let id_iter = stmt.query_map([slug], |row| row.get::<usize, String>(0))?;
73 row.get::<usize, String>(0)
74 })?;
75 72
76 for id in id_iter { 73 for id in id_iter {
77 mod_id = id?; 74 mod_id = id?;
78 }; 75 }
79 } 76 }
80 77
81 if mod_id.is_empty() { return Err(MLError::new(ErrorType::DBError, "GI_MOD_NOT_FOUND")) }; 78 if mod_id.is_empty() {
79 return Err(MLError::new(ErrorType::DBError, "GI_MOD_NOT_FOUND"));
80 };
82 81
83 Ok(mod_id) 82 Ok(mod_id)
84} 83}
@@ -91,17 +90,23 @@ pub struct ModInfo {
91pub fn mods_get_info(config: Cfg, id: &str) -> MLE<ModInfo> { 90pub fn mods_get_info(config: Cfg, id: &str) -> MLE<ModInfo> {
92 let data = devdir(format!("{}/data.db", config.data).as_str()); 91 let data = devdir(format!("{}/data.db", config.data).as_str());
93 let connection = Connection::open(data)?; 92 let connection = Connection::open(data)?;
94 93
95 let mut mod_info: Option<ModInfo> = None; 94 let mut mod_info: Option<ModInfo> = None;
96 let mut stmt = connection.prepare("SELECT title, slug FROM mods WHERE id = ?")?; 95 let mut stmt = connection.prepare("SELECT title, slug FROM mods WHERE id = ?")?;
97 let name_iter = stmt.query_map([id], |row| { 96 let name_iter = stmt.query_map([id], |row| {
98 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?]) 97 Ok(vec![
98 row.get::<usize, String>(0)?,
99 row.get::<usize, String>(1)?,
100 ])
99 })?; 101 })?;
100 102
101 for info in name_iter { 103 for info in name_iter {
102 let i = info?; 104 let i = info?;
103 mod_info = Some(ModInfo { title: String::from(&i[0]), slug: String::from(&i[1]) }); 105 mod_info = Some(ModInfo {
104 }; 106 title: String::from(&i[0]),
107 slug: String::from(&i[1]),
108 });
109 }
105 110
106 match mod_info.is_none() { 111 match mod_info.is_none() {
107 true => Err(MLError::new(ErrorType::DBError, "GN_MOD_NOT_FOUND")), 112 true => Err(MLError::new(ErrorType::DBError, "GN_MOD_NOT_FOUND")),
@@ -110,7 +115,6 @@ pub fn mods_get_info(config: Cfg, id: &str) -> MLE<ModInfo> {
110} 115}
111 116
112pub fn mods_remove(config: Cfg, id: String) -> MLE<()> { 117pub fn mods_remove(config: Cfg, id: String) -> MLE<()> {
113
114 println!("Removing mod {} from database", id); 118 println!("Removing mod {} from database", id);
115 119
116 let data = devdir(format!("{}/data.db", config.data).as_str()); 120 let data = devdir(format!("{}/data.db", config.data).as_str());
@@ -131,27 +135,42 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
131 let data = devdir(format!("{}/data.db", config.data).as_str()); 135 let data = devdir(format!("{}/data.db", config.data).as_str());
132 let connection = Connection::open(data)?; 136 let connection = Connection::open(data)?;
133 137
134 if mods.is_empty() { return Err(MLError::new(ErrorType::ArgumentError, "MODS_NO_INPUT")); } 138 if mods.is_empty() {
139 return Err(MLError::new(ErrorType::ArgumentError, "MODS_NO_INPUT"));
140 }
135 141
136 let mut wherestr = String::from("WHERE"); 142 let mut wherestr = String::from("WHERE");
137 for (i, id) in mods.iter().enumerate() { 143 for (i, id) in mods.iter().enumerate() {
138 let mut or = " OR"; 144 let mut or = " OR";
139 if i == mods.len() - 1 { or = "" }; 145 if i == mods.len() - 1 {
146 or = ""
147 };
140 wherestr = format!("{} id = '{}'{}", wherestr, id, or); 148 wherestr = format!("{} id = '{}'{}", wherestr, id, or);
141 } 149 }
142 150
143 let mut versionmaps: Vec<DBModlistVersions> = Vec::new(); 151 let mut versionmaps: Vec<DBModlistVersions> = Vec::new();
144 let mut stmt = connection.prepare(format!("SELECT id, versions, title FROM mods {}", wherestr).as_str())?; 152 let mut stmt = connection
153 .prepare(format!("SELECT id, versions, title FROM mods {}", wherestr).as_str())?;
145 let id_iter = stmt.query_map([], |row| { 154 let id_iter = stmt.query_map([], |row| {
146 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?, row.get::<usize, String>(2)?]) 155 Ok(vec![
156 row.get::<usize, String>(0)?,
157 row.get::<usize, String>(1)?,
158 row.get::<usize, String>(2)?,
159 ])
147 })?; 160 })?;
148 161
149 for ver in id_iter { 162 for ver in id_iter {
150 let version = ver?; 163 let version = ver?;
151 println!("\t({}) Get versions from the database", String::from(&version[2])); 164 println!(
165 "\t({}) Get versions from the database",
166 String::from(&version[2])
167 );
152 //println!("Found versions {} for mod {}", version[1], version[0]); 168 //println!("Found versions {} for mod {}", version[1], version[0]);
153 versionmaps.push(DBModlistVersions { mod_id: String::from(&version[0]), versions: String::from(&version[1]) }) 169 versionmaps.push(DBModlistVersions {
154 }; 170 mod_id: String::from(&version[0]),
171 versions: String::from(&version[1]),
172 })
173 }
155 174
156 match versionmaps.is_empty() { 175 match versionmaps.is_empty() {
157 true => Err(MLError::new(ErrorType::DBError, "MODS_MODS_NOT_FOUND")), 176 true => Err(MLError::new(ErrorType::DBError, "MODS_MODS_NOT_FOUND")),
@@ -160,16 +179,37 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
160} 179}
161 180
162//userlist 181//userlist
163pub fn userlist_insert(config: Cfg, list_id: &str, mod_id: &str, current_version: &str, applicable_versions: Vec<String>, current_link: &str, set_version: bool) -> MLE<()> { 182pub fn userlist_insert(
183 config: Cfg,
184 list_id: &str,
185 mod_id: &str,
186 current_version: &str,
187 applicable_versions: Vec<String>,
188 current_link: &str,
189 set_version: bool,
190) -> MLE<()> {
164 let data = devdir(format!("{}/data.db", config.data).as_str()); 191 let data = devdir(format!("{}/data.db", config.data).as_str());
165 let connection = Connection::open(data)?; 192 let connection = Connection::open(data)?;
166 193
167 let sv = match set_version { 194 let sv = match set_version {
168 true => "1", 195 true => "1",
169 false => "0", 196 false => "0",
170 }; 197 };
171 198
172 connection.execute(format!("INSERT INTO {} VALUES (?1, ?2, ?3, ?4, 'NONE', ?5)", list_id).as_str(), [mod_id, current_version, applicable_versions.join("|").as_str(), current_link, sv])?; 199 connection.execute(
200 format!(
201 "INSERT INTO {} VALUES (?1, ?2, ?3, ?4, 'NONE', ?5)",
202 list_id
203 )
204 .as_str(),
205 [
206 mod_id,
207 current_version,
208 applicable_versions.join("|").as_str(),
209 current_link,
210 sv,
211 ],
212 )?;
173 213
174 Ok(()) 214 Ok(())
175} 215}
@@ -180,14 +220,12 @@ pub fn userlist_get_all_ids(config: Cfg, list_id: String) -> MLE<Vec<String>> {
180 220
181 let mut mod_ids: Vec<String> = Vec::new(); 221 let mut mod_ids: Vec<String> = Vec::new();
182 let mut stmt = connection.prepare(format!("SELECT mod_id FROM {}", list_id).as_str())?; 222 let mut stmt = connection.prepare(format!("SELECT mod_id FROM {}", list_id).as_str())?;
183 let id_iter = stmt.query_map([], |row| { 223 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
184 row.get::<usize, String>(0)
185 })?;
186 224
187 for id in id_iter { 225 for id in id_iter {
188 //println!("Found id {:?}", id.as_ref().unwrap()); 226 //println!("Found id {:?}", id.as_ref().unwrap());
189 mod_ids.push(id?) 227 mod_ids.push(id?)
190 }; 228 }
191 229
192 match mod_ids.is_empty() { 230 match mod_ids.is_empty() {
193 true => Err(MLError::new(ErrorType::DBError, "NO_MODS")), 231 true => Err(MLError::new(ErrorType::DBError, "NO_MODS")),
@@ -199,24 +237,34 @@ pub fn userlist_remove(config: Cfg, list_id: &str, mod_id: &str) -> MLE<()> {
199 let data = devdir(format!("{}/data.db", config.data).as_str()); 237 let data = devdir(format!("{}/data.db", config.data).as_str());
200 let connection = Connection::open(data)?; 238 let connection = Connection::open(data)?;
201 239
202 connection.execute(format!("DELETE FROM {} WHERE mod_id = ?", list_id).as_str(), [mod_id])?; 240 connection.execute(
241 format!("DELETE FROM {} WHERE mod_id = ?", list_id).as_str(),
242 [mod_id],
243 )?;
203 Ok(()) 244 Ok(())
204} 245}
205 246
206 247pub fn userlist_get_applicable_versions(
207pub fn userlist_get_applicable_versions(config: Cfg, list_id: String, mod_id: String) -> MLE<String> { 248 config: Cfg,
249 list_id: String,
250 mod_id: String,
251) -> MLE<String> {
208 let data = devdir(format!("{}/data.db", config.data).as_str()); 252 let data = devdir(format!("{}/data.db", config.data).as_str());
209 let connection = Connection::open(data).unwrap(); 253 let connection = Connection::open(data).unwrap();
210 254
211 let mut version: String = String::new(); 255 let mut version: String = String::new();
212 let mut stmt = connection.prepare(format!("SELECT applicable_versions FROM {} WHERE mod_id = ?", list_id).as_str())?; 256 let mut stmt = connection.prepare(
213 let ver_iter = stmt.query_map([mod_id], |row| { 257 format!(
214 row.get::<usize, String>(0) 258 "SELECT applicable_versions FROM {} WHERE mod_id = ?",
215 })?; 259 list_id
260 )
261 .as_str(),
262 )?;
263 let ver_iter = stmt.query_map([mod_id], |row| row.get::<usize, String>(0))?;
216 264
217 for ver in ver_iter { 265 for ver in ver_iter {
218 version = ver?; 266 version = ver?;
219 }; 267 }
220 268
221 match version.is_empty() { 269 match version.is_empty() {
222 true => Err(MLError::new(ErrorType::DBError, "GAV_MOD_NOT_FOUND")), 270 true => Err(MLError::new(ErrorType::DBError, "GAV_MOD_NOT_FOUND")),
@@ -224,22 +272,31 @@ pub fn userlist_get_applicable_versions(config: Cfg, list_id: String, mod_id: St
224 } 272 }
225} 273}
226 274
227pub fn userlist_get_all_applicable_versions_with_mods(config: Cfg, list_id: String) -> MLE<Vec<(String, String)>> { 275pub fn userlist_get_all_applicable_versions_with_mods(
276 config: Cfg,
277 list_id: String,
278) -> MLE<Vec<(String, String)>> {
228 let data = devdir(format!("{}/data.db", config.data).as_str()); 279 let data = devdir(format!("{}/data.db", config.data).as_str());
229 let connection = Connection::open(data)?; 280 let connection = Connection::open(data)?;
230 281
231 let mut versions: Vec<(String, String)> = Vec::new(); 282 let mut versions: Vec<(String, String)> = Vec::new();
232 let mut stmt = connection.prepare(format!("SELECT mod_id, applicable_versions FROM {}", list_id).as_str())?; 283 let mut stmt = connection
284 .prepare(format!("SELECT mod_id, applicable_versions FROM {}", list_id).as_str())?;
233 let id_iter = stmt.query_map([], |row| { 285 let id_iter = stmt.query_map([], |row| {
234 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?]) 286 Ok(vec![
287 row.get::<usize, String>(0)?,
288 row.get::<usize, String>(1)?,
289 ])
235 })?; 290 })?;
236 291
237 for ver in id_iter { 292 for ver in id_iter {
238 let out = ver?; 293 let out = ver?;
239 versions.push((out[0].to_owned(), out[1].to_owned())); 294 versions.push((out[0].to_owned(), out[1].to_owned()));
240 }; 295 }
241 296
242 if versions.is_empty() { return Err(MLError::new(ErrorType::DBError, "NO_MODS_ON_LIST")); }; 297 if versions.is_empty() {
298 return Err(MLError::new(ErrorType::DBError, "NO_MODS_ON_LIST"));
299 };
243 300
244 Ok(versions) 301 Ok(versions)
245} 302}
@@ -249,14 +306,13 @@ pub fn userlist_get_current_version(config: Cfg, list_id: &str, mod_id: &str) ->
249 let connection = Connection::open(data).unwrap(); 306 let connection = Connection::open(data).unwrap();
250 307
251 let mut version: String = String::new(); 308 let mut version: String = String::new();
252 let mut stmt = connection.prepare(format!("SELECT current_version FROM {} WHERE mod_id = ?", list_id).as_str())?; 309 let mut stmt = connection
253 let ver_iter = stmt.query_map([&mod_id], |row| { 310 .prepare(format!("SELECT current_version FROM {} WHERE mod_id = ?", list_id).as_str())?;
254 row.get::<usize, String>(0) 311 let ver_iter = stmt.query_map([&mod_id], |row| row.get::<usize, String>(0))?;
255 })?;
256 312
257 for ver in ver_iter { 313 for ver in ver_iter {
258 version = ver?; 314 version = ver?;
259 }; 315 }
260 316
261 match version.is_empty() { 317 match version.is_empty() {
262 true => Err(MLError::new(ErrorType::DBError, "GCV_MOD_NOT_FOUND")), 318 true => Err(MLError::new(ErrorType::DBError, "GCV_MOD_NOT_FOUND")),
@@ -264,63 +320,88 @@ pub fn userlist_get_current_version(config: Cfg, list_id: &str, mod_id: &str) ->
264 } 320 }
265} 321}
266 322
267pub fn userlist_get_all_current_version_ids(config: Cfg, list_id: String) -> Result<Vec<String>, Box<dyn std::error::Error>> { 323pub fn userlist_get_all_current_version_ids(
324 config: Cfg,
325 list_id: String,
326) -> Result<Vec<String>, Box<dyn std::error::Error>> {
268 let data = devdir(format!("{}/data.db", config.data).as_str()); 327 let data = devdir(format!("{}/data.db", config.data).as_str());
269 let connection = Connection::open(data)?; 328 let connection = Connection::open(data)?;
270 329
271 let mut versions: Vec<String> = Vec::new(); 330 let mut versions: Vec<String> = Vec::new();
272 let mut stmt = connection.prepare(format!("SELECT current_version FROM {}", list_id).as_str())?; 331 let mut stmt =
273 let id_iter = stmt.query_map([], |row| { 332 connection.prepare(format!("SELECT current_version FROM {}", list_id).as_str())?;
274 row.get::<usize, String>(0) 333 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
275 })?;
276 334
277 for id in id_iter { 335 for id in id_iter {
278 versions.push(id?); 336 versions.push(id?);
279 }; 337 }
280 338
281 if versions.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::Other, "NO_MODS_ON_LIST"))); }; 339 if versions.is_empty() {
340 return Err(Box::new(std::io::Error::new(
341 ErrorKind::Other,
342 "NO_MODS_ON_LIST",
343 )));
344 };
282 345
283 Ok(versions) 346 Ok(versions)
284} 347}
285 348
286pub fn userlist_get_all_current_versions_with_mods(config: Cfg, list_id: String) -> Result<Vec<(String, String)>, Box<dyn std::error::Error>> { 349pub fn userlist_get_all_current_versions_with_mods(
350 config: Cfg,
351 list_id: String,
352) -> Result<Vec<(String, String)>, Box<dyn std::error::Error>> {
287 let data = devdir(format!("{}/data.db", config.data).as_str()); 353 let data = devdir(format!("{}/data.db", config.data).as_str());
288 let connection = Connection::open(data)?; 354 let connection = Connection::open(data)?;
289 355
290 let mut versions: Vec<(String, String)> = Vec::new(); 356 let mut versions: Vec<(String, String)> = Vec::new();
291 let mut stmt = connection.prepare(format!("SELECT mod_id, current_version FROM {}", list_id).as_str())?; 357 let mut stmt =
358 connection.prepare(format!("SELECT mod_id, current_version FROM {}", list_id).as_str())?;
292 let id_iter = stmt.query_map([], |row| { 359 let id_iter = stmt.query_map([], |row| {
293 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?]) 360 Ok(vec![
361 row.get::<usize, String>(0)?,
362 row.get::<usize, String>(1)?,
363 ])
294 })?; 364 })?;
295 365
296 for ver in id_iter { 366 for ver in id_iter {
297 let out = ver?; 367 let out = ver?;
298 versions.push((out[0].to_owned(), out[1].to_owned())); 368 versions.push((out[0].to_owned(), out[1].to_owned()));
299 }; 369 }
300 370
301 if versions.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::Other, "NO_MODS_ON_LIST"))); }; 371 if versions.is_empty() {
372 return Err(Box::new(std::io::Error::new(
373 ErrorKind::Other,
374 "NO_MODS_ON_LIST",
375 )));
376 };
302 377
303 Ok(versions) 378 Ok(versions)
304} 379}
305 380
306pub fn userlist_get_set_version(config:Cfg, list_id: &str, mod_id: &str) -> MLE<bool> { 381pub fn userlist_get_set_version(config: Cfg, list_id: &str, mod_id: &str) -> MLE<bool> {
307 let data = devdir(format!("{}/data.db", config.data).as_str()); 382 let data = devdir(format!("{}/data.db", config.data).as_str());
308 let connection = Connection::open(data).unwrap(); 383 let connection = Connection::open(data).unwrap();
309 384
310 let mut set_version: bool = false; 385 let mut set_version: bool = false;
311 let mut stmt = connection.prepare(format!("SELECT set_version FROM {} WHERE mod_id = ?", list_id).as_str())?; 386 let mut stmt = connection
312 let ver_iter = stmt.query_map([&mod_id], |row| { 387 .prepare(format!("SELECT set_version FROM {} WHERE mod_id = ?", list_id).as_str())?;
313 row.get::<usize, bool>(0) 388 let ver_iter = stmt.query_map([&mod_id], |row| row.get::<usize, bool>(0))?;
314 })?;
315 389
316 for ver in ver_iter { 390 for ver in ver_iter {
317 set_version = ver?; 391 set_version = ver?;
318 }; 392 }
319 393
320 Ok(set_version) 394 Ok(set_version)
321} 395}
322 396
323pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: String, versions: String, link: String, mod_id: String) -> MLE<()> { 397pub fn userlist_change_versions(
398 config: Cfg,
399 list_id: String,
400 current_version: String,
401 versions: String,
402 link: String,
403 mod_id: String,
404) -> MLE<()> {
324 let data = devdir(format!("{}/data.db", config.data).as_str()); 405 let data = devdir(format!("{}/data.db", config.data).as_str());
325 let connection = Connection::open(data)?; 406 let connection = Connection::open(data)?;
326 407
@@ -328,33 +409,45 @@ pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: S
328 Ok(()) 409 Ok(())
329} 410}
330 411
331pub fn userlist_add_disabled_versions(config: Cfg, list_id: String, disabled_version: String, mod_id: String) -> MLE<()> { 412pub fn userlist_add_disabled_versions(
413 config: Cfg,
414 list_id: String,
415 disabled_version: String,
416 mod_id: String,
417) -> MLE<()> {
332 let data = devdir(format!("{}/data.db", config.data).as_str()); 418 let data = devdir(format!("{}/data.db", config.data).as_str());
333 let connection = Connection::open(data)?; 419 let connection = Connection::open(data)?;
334 420
335 let currently_disabled_versions = userlist_get_disabled_versions(config, String::from(&list_id), String::from(&mod_id))?; 421 let currently_disabled_versions =
336 let disabled_versions = match currently_disabled_versions == "NONE" { 422 userlist_get_disabled_versions(config, String::from(&list_id), String::from(&mod_id))?;
423 let disabled_versions = match currently_disabled_versions == "NONE" {
337 true => disabled_version, 424 true => disabled_version,
338 false => format!("{}|{}", currently_disabled_versions, disabled_version), 425 false => format!("{}|{}", currently_disabled_versions, disabled_version),
339 }; 426 };
340 427
341 connection.execute(format!("UPDATE {} SET disabled_versions = ?1 WHERE mod_id = ?2", list_id).as_str(), [disabled_versions, mod_id])?; 428 connection.execute(
429 format!(
430 "UPDATE {} SET disabled_versions = ?1 WHERE mod_id = ?2",
431 list_id
432 )
433 .as_str(),
434 [disabled_versions, mod_id],
435 )?;
342 Ok(()) 436 Ok(())
343} 437}
344 438
345pub fn userlist_get_disabled_versions(config:Cfg, list_id: String, mod_id: String) -> MLE<String> { 439pub fn userlist_get_disabled_versions(config: Cfg, list_id: String, mod_id: String) -> MLE<String> {
346 let data = devdir(format!("{}/data.db", config.data).as_str()); 440 let data = devdir(format!("{}/data.db", config.data).as_str());
347 let connection = Connection::open(data).unwrap(); 441 let connection = Connection::open(data).unwrap();
348 442
349 let mut version: String = String::new(); 443 let mut version: String = String::new();
350 let mut stmt = connection.prepare(format!("SELECT disabled_versions FROM {} WHERE mod_id = ?", list_id).as_str())?; 444 let mut stmt = connection
351 let ver_iter = stmt.query_map([mod_id], |row| { 445 .prepare(format!("SELECT disabled_versions FROM {} WHERE mod_id = ?", list_id).as_str())?;
352 row.get::<usize, String>(0) 446 let ver_iter = stmt.query_map([mod_id], |row| row.get::<usize, String>(0))?;
353 })?;
354 447
355 for ver in ver_iter { 448 for ver in ver_iter {
356 version = ver?; 449 version = ver?;
357 }; 450 }
358 451
359 match version.is_empty() { 452 match version.is_empty() {
360 true => Err(MLError::new(ErrorType::DBError, "GDV_MOD_NOT_FOUND")), 453 true => Err(MLError::new(ErrorType::DBError, "GDV_MOD_NOT_FOUND")),
@@ -362,36 +455,57 @@ pub fn userlist_get_disabled_versions(config:Cfg, list_id: String, mod_id: Strin
362 } 455 }
363} 456}
364 457
365pub fn userlist_get_all_downloads(config: Cfg, list_id: String) -> Result<Vec<String>, Box<dyn std::error::Error>> { 458pub fn userlist_get_all_downloads(
459 config: Cfg,
460 list_id: String,
461) -> Result<Vec<String>, Box<dyn std::error::Error>> {
366 let data = devdir(format!("{}/data.db", config.data).as_str()); 462 let data = devdir(format!("{}/data.db", config.data).as_str());
367 let connection = Connection::open(data).unwrap(); 463 let connection = Connection::open(data).unwrap();
368 464
369 let mut links: Vec<String> = Vec::new(); 465 let mut links: Vec<String> = Vec::new();
370 let mut stmt = connection.prepare(format!("SELECT current_download FROM {}", list_id).as_str())?; 466 let mut stmt =
371 let link_iter = stmt.query_map([], |row| { 467 connection.prepare(format!("SELECT current_download FROM {}", list_id).as_str())?;
372 row.get::<usize, String>(0) 468 let link_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
373 })?;
374 469
375 for link in link_iter { 470 for link in link_iter {
376 let l = link?; 471 let l = link?;
377 println!("Found link {}", String::from(&l)); 472 println!("Found link {}", String::from(&l));
378 links.push(l) 473 links.push(l)
379 }; 474 }
380 475
381 if links.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::Other, "NO_MODS_ON_LIST"))); }; 476 if links.is_empty() {
477 return Err(Box::new(std::io::Error::new(
478 ErrorKind::Other,
479 "NO_MODS_ON_LIST",
480 )));
481 };
382 482
383 Ok(links) 483 Ok(links)
384} 484}
385 485
386//lists 486//lists
387///Inserts into lists table and creates new table 487///Inserts into lists table and creates new table
388pub fn lists_insert(config: Cfg, id: String, mc_version: String, mod_loader: Modloader, download_folder: String) -> MLE<()> { 488pub fn lists_insert(
489 config: Cfg,
490 id: String,
491 mc_version: String,
492 mod_loader: Modloader,
493 download_folder: String,
494) -> MLE<()> {
389 println!("Creating list {}", id); 495 println!("Creating list {}", id);
390 496
391 let data = devdir(format!("{}/data.db", config.data).as_str()); 497 let data = devdir(format!("{}/data.db", config.data).as_str());
392 let connection = Connection::open(data)?; 498 let connection = Connection::open(data)?;
393 499
394 connection.execute("INSERT INTO lists VALUES (?1, ?2, ?3, ?4)", [id.clone(), mc_version, mod_loader.to_string(), download_folder])?; 500 connection.execute(
501 "INSERT INTO lists VALUES (?1, ?2, ?3, ?4)",
502 [
503 id.clone(),
504 mc_version,
505 mod_loader.to_string(),
506 download_folder,
507 ],
508 )?;
395 connection.execute(format!("CREATE TABLE {}( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'current_download' TEXT, 'disabled_versions' TEXT DEFAULT 'NONE', 'set_version' INTEGER, CONSTRAINT {}_PK PRIMARY KEY (mod_id) )", id, id).as_str(), [])?; 509 connection.execute(format!("CREATE TABLE {}( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'current_download' TEXT, 'disabled_versions' TEXT DEFAULT 'NONE', 'set_version' INTEGER, CONSTRAINT {}_PK PRIMARY KEY (mod_id) )", id, id).as_str(), [])?;
396 510
397 Ok(()) 511 Ok(())
@@ -410,20 +524,37 @@ pub fn lists_get(config: Cfg, list_id: String) -> MLE<List> {
410 let data = devdir(format!("{}/data.db", config.data).as_str()); 524 let data = devdir(format!("{}/data.db", config.data).as_str());
411 let connection = Connection::open(data).unwrap(); 525 let connection = Connection::open(data).unwrap();
412 526
413 let mut list = List { id: String::new(), mc_version: String::new(), modloader: Modloader::Fabric, download_folder: String::new() }; 527 let mut list = List {
414 let mut stmt = connection.prepare("SELECT mc_version, modloader, download_folder FROM lists WHERE id = ?")?; 528 id: String::new(),
529 mc_version: String::new(),
530 modloader: Modloader::Fabric,
531 download_folder: String::new(),
532 };
533 let mut stmt = connection
534 .prepare("SELECT mc_version, modloader, download_folder FROM lists WHERE id = ?")?;
415 535
416 let list_iter = stmt.query_map([&list_id], |row| { 536 let list_iter = stmt.query_map([&list_id], |row| {
417 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?, row.get::<usize, String>(2)?]) 537 Ok(vec![
538 row.get::<usize, String>(0)?,
539 row.get::<usize, String>(1)?,
540 row.get::<usize, String>(2)?,
541 ])
418 })?; 542 })?;
419 543
420 for l in list_iter { 544 for l in list_iter {
421 let li = l?; 545 let li = l?;
422 list = List { id: String::from(&list_id), mc_version: String::from(&li[0]), modloader: Modloader::from(&li[1])?, download_folder: String::from(&li[2]) }; 546 list = List {
423 }; 547 id: String::from(&list_id),
548 mc_version: String::from(&li[0]),
549 modloader: Modloader::from(&li[1])?,
550 download_folder: String::from(&li[2]),
551 };
552 }
553
554 if list.id.is_empty() {
555 return Err(MLError::new(ErrorType::DBError, "LIST_NOT_FOUND"));
556 }
424 557
425 if list.id.is_empty() { return Err(MLError::new(ErrorType::DBError, "LIST_NOT_FOUND")); }
426
427 Ok(list) 558 Ok(list)
428} 559}
429 560
@@ -431,23 +562,24 @@ pub fn lists_version(config: Cfg, list_id: &str, version: &str) -> MLE<()> {
431 let data = devdir(format!("{}/data.db", config.data).as_str()); 562 let data = devdir(format!("{}/data.db", config.data).as_str());
432 let connection = Connection::open(data).unwrap(); 563 let connection = Connection::open(data).unwrap();
433 564
434 connection.execute("UPDATE lists SET mc_version = ? WHERE id = ?", [version, list_id])?; 565 connection.execute(
566 "UPDATE lists SET mc_version = ? WHERE id = ?",
567 [version, list_id],
568 )?;
435 Ok(()) 569 Ok(())
436} 570}
437 571
438pub fn lists_get_all_ids(config: Cfg) -> MLE<Vec<String>> { 572pub fn lists_get_all_ids(config: Cfg) -> MLE<Vec<String>> {
439 let data = devdir(format!("{}/data.db", config.data).as_str()); 573 let data = devdir(format!("{}/data.db", config.data).as_str());
440 let connection = Connection::open(data).unwrap(); 574 let connection = Connection::open(data).unwrap();
441 575
442 let mut list_ids: Vec<String> = Vec::new(); 576 let mut list_ids: Vec<String> = Vec::new();
443 let mut stmt = connection.prepare("SELECT id FROM lists")?; 577 let mut stmt = connection.prepare("SELECT id FROM lists")?;
444 let id_iter = stmt.query_map([], |row| { 578 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
445 row.get::<usize, String>(0)
446 })?;
447 579
448 for id in id_iter { 580 for id in id_iter {
449 list_ids.push(id?) 581 list_ids.push(id?)
450 }; 582 }
451 583
452 match list_ids.is_empty() { 584 match list_ids.is_empty() {
453 true => Err(MLError::new(ErrorType::DBError, "NO_LISTS")), 585 true => Err(MLError::new(ErrorType::DBError, "NO_LISTS")),
@@ -460,35 +592,50 @@ pub fn config_change_current_list(config: Cfg, id: String) -> MLE<()> {
460 let data = devdir(format!("{}/data.db", config.data).as_str()); 592 let data = devdir(format!("{}/data.db", config.data).as_str());
461 let connection = Connection::open(data)?; 593 let connection = Connection::open(data)?;
462 594
463 connection.execute("UPDATE user_config SET value = ? WHERE id = 'current_list'", [id])?; 595 connection.execute(
596 "UPDATE user_config SET value = ? WHERE id = 'current_list'",
597 [id],
598 )?;
464 Ok(()) 599 Ok(())
465} 600}
466 601
467pub fn config_get_current_list(config: Cfg) -> MLE<String> { 602pub fn config_get_current_list(config: Cfg) -> MLE<String> {
468 let data = devdir(format!("{}/data.db", config.data).as_str()); 603 let data = devdir(format!("{}/data.db", config.data).as_str());
469 let connection = Connection::open(data).unwrap(); 604 let connection = Connection::open(data).unwrap();
470 605
471 let mut list_id = String::new(); 606 let mut list_id = String::new();
472 let mut stmt = connection.prepare("SELECT value FROM user_config WHERE id = 'current_list'")?; 607 let mut stmt = connection.prepare("SELECT value FROM user_config WHERE id = 'current_list'")?;
473 let list_iter = stmt.query_map([], |row| { 608 let list_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
474 row.get::<usize, String>(0)
475 })?;
476 609
477 for list in list_iter { 610 for list in list_iter {
478 list_id = list?; 611 list_id = list?;
479 }; 612 }
613
614 if list_id.is_empty() {
615 return Err(MLError::new(ErrorType::DBError, "NO_CURRENT_LIST"));
616 }
480 617
481 if list_id.is_empty() { return Err(MLError::new(ErrorType::DBError, "NO_CURRENT_LIST")); }
482
483 Ok(list_id) 618 Ok(list_id)
484} 619}
485 620
486//SETUP(UPDATES) 621//SETUP(UPDATES)
487pub fn s_userlist_update_download(config: Cfg, list_id: String, mod_id: String, link: String) -> Result<(), Box<dyn std::error::Error>> { 622pub fn s_userlist_update_download(
623 config: Cfg,
624 list_id: String,
625 mod_id: String,
626 link: String,
627) -> Result<(), Box<dyn std::error::Error>> {
488 let data = devdir(format!("{}/data.db", config.data).as_str()); 628 let data = devdir(format!("{}/data.db", config.data).as_str());
489 let connection = Connection::open(data)?; 629 let connection = Connection::open(data)?;
490 630
491 connection.execute(format!("UPDATE {} SET current_download = ?1 WHERE mod_id = ?2", list_id).as_str(), [link, mod_id])?; 631 connection.execute(
632 format!(
633 "UPDATE {} SET current_download = ?1 WHERE mod_id = ?2",
634 list_id
635 )
636 .as_str(),
637 [link, mod_id],
638 )?;
492 Ok(()) 639 Ok(())
493} 640}
494 641
@@ -496,7 +643,10 @@ pub fn s_config_create_version(config: Cfg) -> Result<(), Box<dyn std::error::Er
496 let data = devdir(format!("{}/data.db", config.data).as_str()); 643 let data = devdir(format!("{}/data.db", config.data).as_str());
497 let connection = Connection::open(data)?; 644 let connection = Connection::open(data)?;
498 645
499 connection.execute("INSERT INTO 'user_config' VALUES ( 'db_version', '0.2' )", ())?; 646 connection.execute(
647 "INSERT INTO 'user_config' VALUES ( 'db_version', '0.2' )",
648 (),
649 )?;
500 Ok(()) 650 Ok(())
501} 651}
502 652
@@ -504,34 +654,46 @@ pub fn s_config_update_version(config: Cfg, ver: String) -> Result<(), Box<dyn s
504 let data = devdir(format!("{}/data.db", config.data).as_str()); 654 let data = devdir(format!("{}/data.db", config.data).as_str());
505 let connection = Connection::open(data)?; 655 let connection = Connection::open(data)?;
506 656
507 connection.execute("UPDATE user_config SET value = ? WHERE id = 'db_version'", [ver])?; 657 connection.execute(
658 "UPDATE user_config SET value = ? WHERE id = 'db_version'",
659 [ver],
660 )?;
508 Ok(()) 661 Ok(())
509} 662}
510 663
511pub fn s_config_get_version(config: Cfg) -> Result<String, Box<dyn std::error::Error>> { 664pub fn s_config_get_version(config: Cfg) -> Result<String, Box<dyn std::error::Error>> {
512 let data = devdir(format!("{}/data.db", config.data).as_str()); 665 let data = devdir(format!("{}/data.db", config.data).as_str());
513 let connection = Connection::open(data)?; 666 let connection = Connection::open(data)?;
514 667
515 let mut version: String = String::new(); 668 let mut version: String = String::new();
516 let mut stmt = connection.prepare("SELECT value FROM user_config WHERE id = 'db_version'")?; 669 let mut stmt = connection.prepare("SELECT value FROM user_config WHERE id = 'db_version'")?;
517 let ver_iter = stmt.query_map([], |row| { 670 let ver_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
518 row.get::<usize, String>(0)
519 })?;
520 671
521 for ver in ver_iter { 672 for ver in ver_iter {
522 version = ver?; 673 version = ver?;
523 }; 674 }
524 675
525 if version.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::Other, "NO_DBVERSION"))); }; 676 if version.is_empty() {
677 return Err(Box::new(std::io::Error::new(
678 ErrorKind::Other,
679 "NO_DBVERSION",
680 )));
681 };
526 Ok(version) 682 Ok(version)
527} 683}
528 684
529pub fn s_insert_column(config: Cfg, table: String, column: String, c_type: String, default: Option<String>) -> Result<(), Box<dyn std::error::Error>> { 685pub fn s_insert_column(
686 config: Cfg,
687 table: String,
688 column: String,
689 c_type: String,
690 default: Option<String>,
691) -> Result<(), Box<dyn std::error::Error>> {
530 let data = devdir(format!("{}/data.db", config.data).as_str()); 692 let data = devdir(format!("{}/data.db", config.data).as_str());
531 let connection = Connection::open(data)?; 693 let connection = Connection::open(data)?;
532 694
533 let mut sql = format!("ALTER TABLE {} ADD '{}' {}", table, column, c_type); 695 let mut sql = format!("ALTER TABLE {} ADD '{}' {}", table, column, c_type);
534 696
535 if default.is_some() { 697 if default.is_some() {
536 sql = format!("{} DEFAULT {}", sql, default.unwrap()); 698 sql = format!("{} DEFAULT {}", sql, default.unwrap());
537 } 699 }
@@ -541,7 +703,6 @@ pub fn s_insert_column(config: Cfg, table: String, column: String, c_type: Strin
541} 703}
542 704
543pub fn db_setup(config: Cfg) -> MLE<()> { 705pub fn db_setup(config: Cfg) -> MLE<()> {
544
545 println!("Initiating database"); 706 println!("Initiating database");
546 707
547 let data = devdir(format!("{}/data.db", config.data).as_str()); 708 let data = devdir(format!("{}/data.db", config.data).as_str());
@@ -554,6 +715,6 @@ pub fn db_setup(config: Cfg) -> MLE<()> {
554 INSERT INTO 'user_config' VALUES ( 'db_version', '0.5' ); 715 INSERT INTO 'user_config' VALUES ( 'db_version', '0.5' );
555 INSERT INTO 'user_config' VALUES ( 'current_list', '...' )", 716 INSERT INTO 'user_config' VALUES ( 'current_list', '...' )",
556 )?; 717 )?;
557 718
558 Ok(()) 719 Ok(())
559} 720}
diff --git a/src/error.rs b/src/error.rs
index 794a919..bd6e3da 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -43,49 +43,70 @@ impl fmt::Display for MLError {
43 ErrorType::LibReq => write!(f, "REQWEST"), 43 ErrorType::LibReq => write!(f, "REQWEST"),
44 ErrorType::LibChrono => write!(f, "Chrono error: {}", self.message), 44 ErrorType::LibChrono => write!(f, "Chrono error: {}", self.message),
45 ErrorType::IoError => write!(f, "IO"), 45 ErrorType::IoError => write!(f, "IO"),
46 ErrorType::Other => write!(f, "OTHER") 46 ErrorType::Other => write!(f, "OTHER"),
47 } 47 }
48 } 48 }
49} 49}
50 50
51impl From<reqwest::Error> for MLError { 51impl From<reqwest::Error> for MLError {
52 fn from(error: reqwest::Error) -> Self { 52 fn from(error: reqwest::Error) -> Self {
53 Self { etype: ErrorType::LibReq, message: error.to_string() } 53 Self {
54 etype: ErrorType::LibReq,
55 message: error.to_string(),
56 }
54 } 57 }
55} 58}
56 59
57impl From<toml::de::Error> for MLError { 60impl From<toml::de::Error> for MLError {
58 fn from(error: toml::de::Error) -> Self { 61 fn from(error: toml::de::Error) -> Self {
59 Self { etype: ErrorType::LibToml, message: error.to_string() } 62 Self {
63 etype: ErrorType::LibToml,
64 message: error.to_string(),
65 }
60 } 66 }
61} 67}
62 68
63impl From<rusqlite::Error> for MLError { 69impl From<rusqlite::Error> for MLError {
64 fn from(error: rusqlite::Error) -> Self { 70 fn from(error: rusqlite::Error) -> Self {
65 Self { etype: ErrorType::LibSql, message: error.to_string() } 71 Self {
72 etype: ErrorType::LibSql,
73 message: error.to_string(),
74 }
66 } 75 }
67} 76}
68 77
69impl From<toml::ser::Error> for MLError { 78impl From<toml::ser::Error> for MLError {
70 fn from(error: toml::ser::Error) -> Self { 79 fn from(error: toml::ser::Error) -> Self {
71 Self { etype: ErrorType::LibToml, message: error.to_string() } 80 Self {
81 etype: ErrorType::LibToml,
82 message: error.to_string(),
83 }
72 } 84 }
73} 85}
74 86
75impl From<chrono::ParseError> for MLError { 87impl From<chrono::ParseError> for MLError {
76 fn from(error: chrono::ParseError) -> Self { 88 fn from(error: chrono::ParseError) -> Self {
77 Self { etype: ErrorType::LibChrono, message: error.to_string() } 89 Self {
90 etype: ErrorType::LibChrono,
91 message: error.to_string(),
92 }
78 } 93 }
79} 94}
80 95
81impl From<std::io::Error> for MLError { 96impl From<std::io::Error> for MLError {
82 fn from(error: std::io::Error) -> Self { 97 fn from(error: std::io::Error) -> Self {
83 Self { etype: ErrorType::IoError, message: error.to_string() } 98 Self {
99 etype: ErrorType::IoError,
100 message: error.to_string(),
101 }
84 } 102 }
85} 103}
86 104
87impl MLError { 105impl MLError {
88 pub fn new(etype: ErrorType, message: &str) -> Self { 106 pub fn new(etype: ErrorType, message: &str) -> Self {
89 Self { etype, message: String::from(message) } 107 Self {
108 etype,
109 message: String::from(message),
110 }
90 } 111 }
91} 112}
diff --git a/src/files.rs b/src/files.rs
index 6519c6a..6160cb4 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -1,11 +1,20 @@
1use std::{fs::{File, read_dir, remove_file, rename}, io::Write, collections::HashMap};
2use futures_util::StreamExt; 1use futures_util::StreamExt;
3use reqwest::Client; 2use reqwest::Client;
4 3use std::{
5use crate::{List, modrinth::Version, db::{userlist_add_disabled_versions, mods_get_info}, config::Cfg, error::{MLE, MLError, ErrorType}}; 4 collections::HashMap,
5 fs::{read_dir, remove_file, rename, File},
6 io::Write,
7};
8
9use crate::{
10 config::Cfg,
11 db::{mods_get_info, userlist_add_disabled_versions},
12 error::{ErrorType, MLError, MLE},
13 modrinth::Version,
14 List,
15};
6 16
7pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> { 17pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> {
8
9 let dl_path = String::from(&list.download_folder); 18 let dl_path = String::from(&list.download_folder);
10 19
11 println!(" └Download mods to {}", dl_path); 20 println!(" └Download mods to {}", dl_path);
@@ -21,7 +30,13 @@ pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>)
21 Ok(e) => e, 30 Ok(e) => e,
22 Err(..) => return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")), 31 Err(..) => return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")),
23 }; 32 };
24 let filename = format!("{}.mr.{}.{}.{}", splitname.join("."), ver.project_id, ver.id, extension); 33 let filename = format!(
34 "{}.mr.{}.{}.{}",
35 splitname.join("."),
36 ver.project_id,
37 ver.id,
38 extension
39 );
25 download_file(primary_file.url, list.clone().download_folder, filename).await?; 40 download_file(primary_file.url, list.clone().download_folder, filename).await?;
26 //tokio::time::sleep(std::time::Duration::new(3, 0)).await; 41 //tokio::time::sleep(std::time::Duration::new(3, 0)).await;
27 println!(" ✓"); 42 println!(" ✓");
@@ -32,10 +47,7 @@ pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>)
32 47
33async fn download_file(url: String, path: String, name: String) -> MLE<()> { 48async fn download_file(url: String, path: String, name: String) -> MLE<()> {
34 let dl_path_file = format!("{}/{}", path, name); 49 let dl_path_file = format!("{}/{}", path, name);
35 let res = Client::new() 50 let res = Client::new().get(String::from(&url)).send().await?;
36 .get(String::from(&url))
37 .send()
38 .await?;
39 51
40 // download chunks 52 // download chunks
41 let mut file = File::create(&dl_path_file)?; 53 let mut file = File::create(&dl_path_file)?;
@@ -49,7 +61,12 @@ async fn download_file(url: String, path: String, name: String) -> MLE<()> {
49 Ok(()) 61 Ok(())
50} 62}
51 63
52pub fn disable_version(config: Cfg, current_list: List, versionid: String, mod_id: String) -> MLE<()> { 64pub fn disable_version(
65 config: Cfg,
66 current_list: List,
67 versionid: String,
68 mod_id: String,
69) -> MLE<()> {
53 //println!("Disabling version {} for mod {}", versionid, mod_id); 70 //println!("Disabling version {} for mod {}", versionid, mod_id);
54 let file = get_file_path(current_list.clone(), String::from(&versionid))?; 71 let file = get_file_path(current_list.clone(), String::from(&versionid))?;
55 let disabled = format!("{}.disabled", file); 72 let disabled = format!("{}.disabled", file);
@@ -63,7 +80,7 @@ pub fn disable_version(config: Cfg, current_list: List, versionid: String, mod_i
63 80
64pub fn delete_version(list: List, version: String) -> MLE<()> { 81pub fn delete_version(list: List, version: String) -> MLE<()> {
65 let file = get_file_path(list, version)?; 82 let file = get_file_path(list, version)?;
66 83
67 remove_file(file)?; 84 remove_file(file)?;
68 85
69 Ok(()) 86 Ok(())
@@ -76,19 +93,24 @@ pub fn get_file_path(list: List, versionid: String) -> MLE<String> {
76 if path.is_file() { 93 if path.is_file() {
77 let pathstr = match path.to_str().ok_or("") { 94 let pathstr = match path.to_str().ok_or("") {
78 Ok(s) => s, 95 Ok(s) => s,
79 Err(..) => return Err(MLError::new(ErrorType::Other, "INVALID_PATH")) 96 Err(..) => return Err(MLError::new(ErrorType::Other, "INVALID_PATH")),
80 }; 97 };
81 let namesplit: Vec<&str> = pathstr.split('.').collect(); 98 let namesplit: Vec<&str> = pathstr.split('.').collect();
82 let ver_id = namesplit[namesplit.len() - 2]; 99 let ver_id = namesplit[namesplit.len() - 2];
83 names.insert(String::from(ver_id), String::from(pathstr)); 100 names.insert(String::from(ver_id), String::from(pathstr));
84 } 101 }
85 }; 102 }
86 103
87 let filename = match names.get(&versionid).ok_or("") { 104 let filename = match names.get(&versionid).ok_or("") {
88 Ok(n) => n, 105 Ok(n) => n,
89 Err(..) => return Err(MLError::new(ErrorType::ArgumentError, "VERSION_NOT_FOUND_IN_FILES")) 106 Err(..) => {
107 return Err(MLError::new(
108 ErrorType::ArgumentError,
109 "VERSION_NOT_FOUND_IN_FILES",
110 ))
111 }
90 }; 112 };
91 113
92 Ok(filename.to_owned()) 114 Ok(filename.to_owned())
93} 115}
94 116
@@ -99,7 +121,10 @@ pub fn get_downloaded_versions(list: List) -> MLE<HashMap<String, String>> {
99 if path.is_file() && path.extension().ok_or("BAH").unwrap() == "jar" { 121 if path.is_file() && path.extension().ok_or("BAH").unwrap() == "jar" {
100 let pathstr = path.to_str().ok_or("BAH").unwrap(); 122 let pathstr = path.to_str().ok_or("BAH").unwrap();
101 let namesplit: Vec<&str> = pathstr.split('.').collect(); 123 let namesplit: Vec<&str> = pathstr.split('.').collect();
102 versions.insert(String::from(namesplit[namesplit.len() - 3]), String::from(namesplit[namesplit.len() - 2])); 124 versions.insert(
125 String::from(namesplit[namesplit.len() - 3]),
126 String::from(namesplit[namesplit.len() - 2]),
127 );
103 } 128 }
104 } 129 }
105 Ok(versions) 130 Ok(versions)
@@ -111,6 +136,6 @@ pub fn clean_list_dir(list: &List) -> MLE<()> {
111 for entry in std::fs::read_dir(dl_path)? { 136 for entry in std::fs::read_dir(dl_path)? {
112 let entry = entry?; 137 let entry = entry?;
113 std::fs::remove_file(entry.path())?; 138 std::fs::remove_file(entry.path())?;
114 }; 139 }
115 Ok(()) 140 Ok(())
116} 141}
diff --git a/src/lib.rs b/src/lib.rs
index 9db907d..43f0fe7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,20 +1,20 @@
1pub mod apis; 1pub mod apis;
2pub mod config;
3pub mod commands; 2pub mod commands;
3pub mod config;
4pub mod db; 4pub mod db;
5pub mod error; 5pub mod error;
6pub mod files; 6pub mod files;
7 7
8use std::{path::Path, fmt::Display}; 8use std::{fmt::Display, path::Path};
9 9
10pub use apis::*; 10pub use apis::*;
11pub use commands::*; 11pub use commands::*;
12use error::{MLE, ErrorType, MLError}; 12use error::{ErrorType, MLError, MLE};
13 13
14#[derive(Debug, Clone, PartialEq, Eq)] 14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum Modloader { 15pub enum Modloader {
16 Fabric, 16 Fabric,
17 Forge 17 Forge,
18} 18}
19 19
20impl Modloader { 20impl Modloader {
@@ -22,7 +22,7 @@ impl Modloader {
22 match string { 22 match string {
23 "forge" => Ok(Modloader::Forge), 23 "forge" => Ok(Modloader::Forge),
24 "fabric" => Ok(Modloader::Fabric), 24 "fabric" => Ok(Modloader::Fabric),
25 _ => Err(MLError::new(ErrorType::ArgumentError, "UNKNOWN_MODLOADER")) 25 _ => Err(MLError::new(ErrorType::ArgumentError, "UNKNOWN_MODLOADER")),
26 } 26 }
27 } 27 }
28} 28}
diff --git a/src/main.rs b/src/main.rs
index e845be1..eb5ee0b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,10 @@
1use clap::{Parser, Subcommand}; 1use clap::{Parser, Subcommand};
2use modlist::{config::Cfg, mod_add, mod_remove, db::{lists_get, config_get_current_list, lists_get_all_ids}, IDSelector, download, update, List, get_current_list, import, devdir, export, list_add, Modloader, list_version, list_remove, list_change}; 2use modlist::{
3 config::Cfg,
4 db::{config_get_current_list, lists_get, lists_get_all_ids},
5 devdir, download, export, get_current_list, import, list_add, list_change, list_remove,
6 list_version, mod_add, mod_remove, update, IDSelector, List, Modloader,
7};
3 8
4//TODO make default list optional 9//TODO make default list optional
5#[derive(Parser)] 10#[derive(Parser)]
@@ -17,13 +22,13 @@ enum Commands {
17 }, 22 },
18 List { 23 List {
19 #[command(subcommand)] 24 #[command(subcommand)]
20 command: ListCommands 25 command: ListCommands,
21 }, 26 },
22 Download { 27 Download {
23 /// download all lists 28 /// download all lists
24 #[arg(short, long)] 29 #[arg(short, long)]
25 all: bool, 30 all: bool,
26 31
27 /// clean all mods before downloading them 32 /// clean all mods before downloading them
28 #[arg(short, long)] 33 #[arg(short, long)]
29 clean: bool, 34 clean: bool,
@@ -36,11 +41,11 @@ enum Commands {
36 /// download all lists 41 /// download all lists
37 #[arg(short, long)] 42 #[arg(short, long)]
38 all: bool, 43 all: bool,
39 44
40 /// directly download updated mods 45 /// directly download updated mods
41 #[arg(short, long)] 46 #[arg(short, long)]
42 download: bool, 47 download: bool,
43 48
44 /// clean all mods before downloading them 49 /// clean all mods before downloading them
45 #[arg(short, long)] 50 #[arg(short, long)]
46 clean: bool, 51 clean: bool,
@@ -59,7 +64,7 @@ enum Commands {
59 }, 64 },
60 Export { 65 Export {
61 /// the list you want to export 66 /// the list you want to export
62 list: Option<String> 67 list: Option<String>,
63 }, 68 },
64} 69}
65 70
@@ -68,7 +73,7 @@ enum ModCommands {
68 Add { 73 Add {
69 /// id of the mod/version 74 /// id of the mod/version
70 id: String, 75 id: String,
71 76
72 /// set id mode to version 77 /// set id mode to version
73 #[arg(short, long)] 78 #[arg(short, long)]
74 version: bool, 79 version: bool,
@@ -83,7 +88,7 @@ enum ModCommands {
83 88
84 /// optional List selection, else default list will be used 89 /// optional List selection, else default list will be used
85 #[arg(short, long)] 90 #[arg(short, long)]
86 list: Option<String> 91 list: Option<String>,
87 }, 92 },
88 Remove { 93 Remove {
89 /// id, name or title of the mod 94 /// id, name or title of the mod
@@ -91,8 +96,8 @@ enum ModCommands {
91 96
92 /// optional List selection, else default list will be used 97 /// optional List selection, else default list will be used
93 #[arg(short, long)] 98 #[arg(short, long)]
94 list: Option<String> 99 list: Option<String>,
95 } 100 },
96} 101}
97 102
98#[derive(Subcommand)] 103#[derive(Subcommand)]
@@ -109,12 +114,12 @@ enum ListCommands {
109 }, 114 },
110 Remove { 115 Remove {
111 /// id, name or title of the list 116 /// id, name or title of the list
112 id: String 117 id: String,
113 }, 118 },
114 List, 119 List,
115 Change { 120 Change {
116 /// id of the list to change to 121 /// id of the list to change to
117 id: String 122 id: String,
118 }, 123 },
119 Version { 124 Version {
120 /// list id 125 /// list id
@@ -129,12 +134,11 @@ enum ListCommands {
129 /// delete disabled versions 134 /// delete disabled versions
130 #[arg(short, long)] 135 #[arg(short, long)]
131 remove: bool, 136 remove: bool,
132 } 137 },
133} 138}
134 139
135#[tokio::main] 140#[tokio::main]
136async fn main() { 141async fn main() {
137
138 let cli = Cli::parse(); 142 let cli = Cli::parse();
139 143
140 let config = Cfg::init("modlist.toml").unwrap(); 144 let config = Cfg::init("modlist.toml").unwrap();
@@ -143,13 +147,22 @@ async fn main() {
143 //TODO setup? maybe setup on install 147 //TODO setup? maybe setup on install
144 match cli.command { 148 match cli.command {
145 Commands::Mod { command } => { 149 Commands::Mod { command } => {
146
147 match command { 150 match command {
148 #[allow(unused_variables)] 151 #[allow(unused_variables)]
149 ModCommands::Add { id, version, list, download, lock } => { 152 ModCommands::Add {
153 id,
154 version,
155 list,
156 download,
157 lock,
158 } => {
150 let listf = match list { 159 let listf = match list {
151 Some(list) => lists_get(config.clone(), list).unwrap(), 160 Some(list) => lists_get(config.clone(), list).unwrap(),
152 None => lists_get(config.clone(), config_get_current_list(config.clone()).unwrap()).unwrap(), 161 None => lists_get(
162 config.clone(),
163 config_get_current_list(config.clone()).unwrap(),
164 )
165 .unwrap(),
153 }; 166 };
154 167
155 let marked_id = match version { 168 let marked_id = match version {
@@ -164,15 +177,24 @@ async fn main() {
164 //TODO add success even if no file found 177 //TODO add success even if no file found
165 let listf = match list { 178 let listf = match list {
166 Some(list) => lists_get(config.clone(), list).unwrap(), 179 Some(list) => lists_get(config.clone(), list).unwrap(),
167 None => lists_get(config.clone(), config_get_current_list(config.clone()).unwrap()).unwrap(), 180 None => lists_get(
181 config.clone(),
182 config_get_current_list(config.clone()).unwrap(),
183 )
184 .unwrap(),
168 }; 185 };
169 mod_remove(config, &id, listf) 186 mod_remove(config, &id, listf)
170 } 187 }
171 } 188 }
172 }, 189 }
173 Commands::List { command } => { 190 Commands::List { command } => {
174 match command { 191 match command {
175 ListCommands::Add { id, directory, modloader, version } => { 192 ListCommands::Add {
193 id,
194 directory,
195 modloader,
196 version,
197 } => {
176 let ml = match modloader { 198 let ml = match modloader {
177 Some(ml) => Modloader::from(&ml).unwrap(), 199 Some(ml) => Modloader::from(&ml).unwrap(),
178 //TODO add default modloader to config 200 //TODO add default modloader to config
@@ -187,23 +209,27 @@ async fn main() {
187 }; 209 };
188 210
189 list_add(config, id, ver, ml, directory) 211 list_add(config, id, ver, ml, directory)
190 }, 212 }
191 ListCommands::Remove { id } => { 213 ListCommands::Remove { id } => list_remove(config, id),
192 list_remove(config, id)
193 },
194 ListCommands::List => { 214 ListCommands::List => {
195 todo!() 215 todo!()
196 },
197 ListCommands::Change { id } => {
198 list_change(config, id)
199 },
200 ListCommands::Version { id, version, download, remove } => {
201 list_version(config, id, version, download, remove).await
202 } 216 }
217 ListCommands::Change { id } => list_change(config, id),
218 ListCommands::Version {
219 id,
220 version,
221 download,
222 remove,
223 } => list_version(config, id, version, download, remove).await,
203 } 224 }
204 }, 225 }
205 //TODO a add specific list 226 //TODO a add specific list
206 Commands::Update { all, download, clean, remove } => { 227 Commands::Update {
228 all,
229 download,
230 clean,
231 remove,
232 } => {
207 let mut liststack: Vec<List> = vec![]; 233 let mut liststack: Vec<List> = vec![];
208 if all { 234 if all {
209 let list_ids = lists_get_all_ids(config.clone()).unwrap(); 235 let list_ids = lists_get_all_ids(config.clone()).unwrap();
@@ -216,21 +242,26 @@ async fn main() {
216 liststack.push(current) 242 liststack.push(current)
217 } 243 }
218 update(config, liststack, clean, download, remove).await 244 update(config, liststack, clean, download, remove).await
219 }, 245 }
220 //TODO add specific list 246 //TODO add specific list
221 Commands::Download { all, clean, remove } => { 247 Commands::Download { all, clean, remove } => download(config, all, clean, remove).await,
222 download(config, all, clean, remove).await
223 },
224 Commands::Import { file, download } => { 248 Commands::Import { file, download } => {
225 let filestr: String = match file { 249 let filestr: String = match file {
226 Some(args) => args, 250 Some(args) => args,
227 None => devdir(dirs::home_dir().unwrap().join("mlexport.toml").into_os_string().into_string().unwrap().as_str()), 251 None => devdir(
252 dirs::home_dir()
253 .unwrap()
254 .join("mlexport.toml")
255 .into_os_string()
256 .into_string()
257 .unwrap()
258 .as_str(),
259 ),
228 }; 260 };
229 261
230 import(config, filestr, download).await 262 import(config, filestr, download).await
231 }, 263 }
232 Commands::Export { list } => { 264 Commands::Export { list } => export(config, list),
233 export(config, list) 265 }
234 }, 266 .unwrap();
235 }.unwrap();
236} 267}