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.rs46
-rw-r--r--src/commands/io.rs78
-rw-r--r--src/commands/list.rs81
-rw-r--r--src/commands/mod.rs16
-rw-r--r--src/commands/modification.rs223
-rw-r--r--src/commands/setup.rs12
-rw-r--r--src/commands/update.rs132
-rw-r--r--src/config.rs18
-rw-r--r--src/db.rs431
-rw-r--r--src/error.rs37
-rw-r--r--src/files.rs59
-rw-r--r--src/input.rs344
-rw-r--r--src/lib.rs29
-rw-r--r--src/main.rs312
15 files changed, 1058 insertions, 816 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 2714630..9434591 100644
--- a/src/commands/download.rs
+++ b/src/commands/download.rs
@@ -1,10 +1,16 @@
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, input::Input}; 2use crate::{
3 3 db::{lists_get, lists_get_all_ids, userlist_get_all_current_versions_with_mods},
4pub async fn download(config: Cfg, input: Input) -> MLE<()> { 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};
5 10
11pub async fn download(config: Cfg, all_lists: bool, clean: bool, delete_old: bool) -> MLE<()> {
6 let mut liststack: Vec<List> = vec![]; 12 let mut liststack: Vec<List> = vec![];
7 if input.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())?;
9 for id in list_ids { 15 for id in list_ids {
10 liststack.push(lists_get(config.clone(), id)?); 16 liststack.push(lists_get(config.clone(), id)?);
@@ -18,7 +24,10 @@ pub async fn download(config: Cfg, input: Input) -> MLE<()> {
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 }?;
@@ -33,31 +42,40 @@ pub async fn download(config: Cfg, input: Input) -> MLE<()> {
33 42
34 let current_download = downloaded_versions.get(&mod_id); 43 let current_download = downloaded_versions.get(&mod_id);
35 44
36 if current_download.is_none() || input.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 input.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 input.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 a3d056f..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::{input::{Input, IoOptions}, db::{lists_get, userlist_get_all_ids, lists_get_all_ids, lists_insert}, config::Cfg, Modloader, List, devdir, error::MLE, mods_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,73 +26,77 @@ 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
35pub async fn io(config: Cfg, input: Input) -> MLE<()> { 48pub fn export(config: Cfg, list: Option<String>) -> MLE<()> {
36
37 match input.clone().io_options.unwrap() {
38 IoOptions::Export => { export(config, input)? },
39 IoOptions::Import => { import(config, input).await? },
40 }
41
42 Ok(())
43}
44
45fn export(config: Cfg, input: Input) -> MLE<()> {
46 let mut list_ids: Vec<String> = vec![]; 49 let mut list_ids: Vec<String> = vec![];
47 if input.all_lists { 50 if list.is_none() {
48 list_ids = lists_get_all_ids(config.clone())?; 51 list_ids = lists_get_all_ids(config.clone())?;
49 } else { 52 } else {
50 list_ids.push(lists_get(config.clone(), input.list.unwrap().id)?.id); 53 list_ids.push(lists_get(config.clone(), list.unwrap())?.id);
51 } 54 }
52 let mut lists: Vec<ExportList> = vec![]; 55 let mut lists: Vec<ExportList> = vec![];
53 for list_id in list_ids { 56 for list_id in list_ids {
54 lists.push(ExportList::from(config.clone(), list_id, true)?); 57 lists.push(ExportList::from(config.clone(), list_id, true)?);
55 } 58 }
56 59
57 let toml = toml::to_string( &Export { lists } )?; 60 let toml = toml::to_string(&Export { lists })?;
58 61
59 let filestr = dirs::home_dir().unwrap().join("mlexport.toml"); 62 let filestr = dirs::home_dir().unwrap().join("mlexport.toml");
60 63
61 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 ))?;
62 file.write_all(toml.as_bytes())?; 67 file.write_all(toml.as_bytes())?;
63 68
64 Ok(()) 69 Ok(())
65} 70}
66 71
67async fn import(config: Cfg, input: Input) -> MLE<()> { 72pub async fn import(config: Cfg, file_str: String, direct_download: bool) -> MLE<()> {
68 73 let mut file = File::open(file_str)?;
69 let filestr: String = match input.file {
70 Some(args) => args,
71 None => devdir(dirs::home_dir().unwrap().join("mlexport.toml").into_os_string().into_string().unwrap().as_str()),
72 };
73
74 let mut file = File::open(filestr)?;
75 let mut content = String::new(); 74 let mut content = String::new();
76 file.read_to_string(&mut content)?; 75 file.read_to_string(&mut content)?;
77 let export: Export = toml::from_str(&content)?; 76 let export: Export = toml::from_str(&content)?;
78 77
79 for exportlist in export.lists { 78 for exportlist in export.lists {
80 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 {
81 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 )?;
82 let mods: Vec<&str> = exportlist.mods.split('|').collect(); 92 let mods: Vec<&str> = exportlist.mods.split('|').collect();
83 let mut mod_ids = vec![]; 93 let mut mod_ids = vec![];
84 for mod_id in mods { 94 for mod_id in mods {
85 mod_ids.push(IDSelector::ModificationID(String::from(mod_id))); 95 mod_ids.push(IDSelector::ModificationID(String::from(mod_id)));
86 }; 96 }
87 //TODO impl set_version and good direct download 97 //TODO impl set_version and good direct download
88 //TODO impl all at once, dafuck 98 //TODO impl all at once, dafuck
89 mods_add(config.clone(), mod_ids, list, input.direct_download, false).await?; 99 mod_add(config.clone(), mod_ids, list, direct_download, false).await?;
90 } 100 }
91 Ok(()) 101 Ok(())
92} 102}
diff --git a/src/commands/list.rs b/src/commands/list.rs
index 8e86973..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, input::{Input, ListOptions}, cmd_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 {
@@ -8,60 +16,55 @@ pub struct List {
8 pub download_folder: String, 16 pub download_folder: String,
9} 17}
10 18
11pub async fn list(config: Cfg, input: Input) -> MLE<()> {
12
13 match input.clone().list_options.unwrap() {
14 ListOptions::Add => {
15 add(config, input)
16 },
17 ListOptions::Change => {
18 change(config, input)
19 },
20 ListOptions::Remove => {
21 remove(config, input)
22 },
23 ListOptions::Version => {
24 version(config, input).await
25 }
26 }
27}
28
29pub fn get_current_list(config: Cfg) -> MLE<List> { 19pub fn get_current_list(config: Cfg) -> MLE<List> {
30 let id = config_get_current_list(config.clone())?; 20 let id = config_get_current_list(config.clone())?;
31 lists_get(config, id) 21 lists_get(config, id)
32} 22}
33 23
34fn add(config: Cfg, input: Input) -> MLE<()> { 24pub fn list_add(
35 let id = input.list_id.unwrap(); 25 config: Cfg,
36 let mc_version = input.list_mcversion.unwrap(); 26 id: String,
37 let mod_loader = input.modloader.unwrap(); 27 mc_version: String,
38 let download_folder = input.directory.unwrap(); 28 modloader: Modloader,
39 lists_insert(config, id, mc_version, mod_loader, download_folder) 29 directory: String,
30) -> MLE<()> {
31 lists_insert(config, id, mc_version, modloader, directory)
40} 32}
41 33
42fn change(config: Cfg, input: Input) -> MLE<()> { 34pub fn list_change(config: Cfg, id: String) -> MLE<()> {
43 println!("Change default list to: {}", input.clone().list.unwrap().id); 35 //TODO check if list exists
44 config_change_current_list(config, input.list.unwrap().id) 36 println!("Change default list to: {}", id);
37 config_change_current_list(config, id)
45} 38}
46 39
47fn remove(config: Cfg, input: Input) -> MLE<()> { 40pub fn list_remove(config: Cfg, id: String) -> MLE<()> {
48 lists_remove(config, input.list.unwrap().id) 41 lists_remove(config, id)
49} 42}
50 43
51///Changing the current lists version and updating it 44///Changing the current lists version and updating it
52/// 45///
53/// #Arguments 46/// #Arguments
54/// 47///
55/// * `config` - The current config 48/// * `config` - The current config
56/// * `args` - All args, to extract the new version 49/// * `args` - All args, to extract the new version
57async fn version(config: Cfg, input: Input) -> MLE<()> { 50pub async fn list_version(
58 println!("Change version for list {} to minecraft version: {}", input.clone().list.unwrap().id, input.clone().list_mcversion.unwrap()); 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 );
59 61
60 lists_version(config.clone(), input.clone().list.ok_or("").unwrap().id, input.clone().list_mcversion.ok_or("").unwrap())?; 62 lists_version(config.clone(), &id, &mc_version)?;
61
62 //Linebreak readability
63 println!("");
64 63
65 println!("Check for updates for new minecraft version in list {}", input.clone().list.unwrap().id); 64 println!(
66 cmd_update(config, vec![input.list.ok_or("").unwrap()], true, input.direct_download, input.delete_old).await 65 "\nCheck for updates for new minecraft version in list {}",
66 id
67 );
68 let list = lists_get(config.clone(), id)?;
69 update(config, vec![list], true, download, delete).await
67} 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 31e50af..ffc4e10 100644
--- a/src/commands/modification.rs
+++ b/src/commands/modification.rs
@@ -1,27 +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}, input::{Input, ModOptions}, 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}
8
9pub async fn modification(config: Cfg, input: Input) -> MLE<()> {
10 match input.clone().mod_options.ok_or("").unwrap() {
11 ModOptions::Add => {
12 add(config, input).await
13 },
14 ModOptions::Remove => {
15 remove(config, input)
16 },
17 }
18}
19
20async fn add(config: Cfg, input: Input) -> MLE<()> {
21
22 mods_add(config, vec![input.mod_id.unwrap()], input.list.unwrap(), input.direct_download, input.set_version).await?;
23
24 Ok(())
25} 17}
26 18
27#[derive(Debug, Clone)] 19#[derive(Debug, Clone)]
@@ -34,10 +26,16 @@ pub struct ProjectInfo {
34 pub download_link: String, 26 pub download_link: String,
35} 27}
36 28
37pub async fn mods_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<()> {
38 println!("Add mods to {}", list.id); 36 println!("Add mods to {}", list.id);
39 println!(" └Add mods:"); 37 println!(" └Add mods:");
40 38
41 let mut mod_ids: Vec<String> = Vec::new(); 39 let mut mod_ids: Vec<String> = Vec::new();
42 let mut ver_ids: Vec<String> = Vec::new(); 40 let mut ver_ids: Vec<String> = Vec::new();
43 41
@@ -50,11 +48,17 @@ pub async fn mods_add(config: Cfg, ids: Vec<IDSelector>, list: List, direct_down
50 } 48 }
51 49
52 let mut projectinfo: Vec<ProjectInfo> = Vec::new(); 50 let mut projectinfo: Vec<ProjectInfo> = Vec::new();
53 if !mod_ids.is_empty() { projectinfo.append(&mut get_mod_infos(config.clone(), mod_ids, list.clone()).await?) }; 51 if !mod_ids.is_empty() {
54 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 };
55 61
56 if projectinfo.is_empty() { return Err(MLError::new(ErrorType::ArgumentError, "NO_IDS?")) };
57
58 let mut downloadstack: Vec<Version> = Vec::new(); 62 let mut downloadstack: Vec<Version> = Vec::new();
59 63
60 //Adding each mod to the lists and downloadstack 64 //Adding each mod to the lists and downloadstack
@@ -63,29 +67,59 @@ pub async fn mods_add(config: Cfg, ids: Vec<IDSelector>, list: List, direct_down
63 } else { 67 } else {
64 println!(" └Insert mods in list {} and save infos", list.id); 68 println!(" └Insert mods in list {} and save infos", list.id);
65 } 69 }
66 70
67 for project in projectinfo { 71 for project in projectinfo {
68 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() {
69 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 ) {
70 Err(e) => { 86 Err(e) => {
71 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);
72 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 {
73 }, 89 Err(MLError::new(
74 Ok(..) => { Ok(..) }, 90 ErrorType::ModError,
91 "MOD_ALREADY_ON_SELECTED_LIST",
92 ))
93 } else {
94 Err(e)
95 }
96 }
97 Ok(..) => Ok(..),
75 }?; 98 }?;
76 99
77 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 ) {
78 Err(e) => { 106 Err(e) => {
79 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" {
80 }, 108 Ok(..)
109 } else {
110 Err(e)
111 }
112 }
81 Ok(..) => Ok(..), 113 Ok(..) => Ok(..),
82 }?; 114 }?;
83 115
84 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 };
85 } 119 }
86 120
87 //Download all the added mods 121 //Download all the added mods
88 if direct_download { 122 if direct_download {
89 download_versions(list.clone(), config.clone(), downloadstack).await?; 123 download_versions(list.clone(), config.clone(), downloadstack).await?;
90 }; 124 };
91 125
@@ -104,7 +138,12 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<String>, list: List) -> MLE<Vec
104 for project in m_projects { 138 for project in m_projects {
105 println!("\t└{}", project.title); 139 println!("\t└{}", project.title);
106 println!("\t └Get versions"); 140 println!("\t └Get versions");
107 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;
108 147
109 let mut available_versions_vec: Vec<String> = Vec::new(); 148 let mut available_versions_vec: Vec<String> = Vec::new();
110 let current_version: Option<Version>; 149 let current_version: Option<Version>;
@@ -113,36 +152,63 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<String>, list: List) -> MLE<Vec
113 let current_id = extract_current_version(available_versions.clone())?; 152 let current_id = extract_current_version(available_versions.clone())?;
114 println!("\t └Current version: {}", current_id); 153 println!("\t └Current version: {}", current_id);
115 154
116 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 );
117 162
118 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;
119 for ver in available_versions { 172 for ver in available_versions {
120 available_versions_vec.push(ver.id); 173 available_versions_vec.push(ver.id);
121 }; 174 }
122
123 projectinfo.push(ProjectInfo { mod_id: project.id, slug: project.slug, title: project.title, current_version, applicable_versions: available_versions_vec, download_link: file })
124 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 })
125 } else { 184 } else {
126 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");
127 current_version = None; 186 current_version = None;
128 file = String::from("NONE"); 187 file = String::from("NONE");
129 available_versions_vec.push(String::from("NONE")); 188 available_versions_vec.push(String::from("NONE"));
130 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 })
131 } 197 }
132 }; 198 }
133 199
134 Ok(projectinfo) 200 Ok(projectinfo)
135} 201}
136 202
137async 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>> {
138 let mut projectinfo: Vec<ProjectInfo> = Vec::new(); 204 let mut projectinfo: Vec<ProjectInfo> = Vec::new();
139 205
140 //Get required information from ver_ids 206 //Get required information from ver_ids
141 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;
142 let mut v_mod_ids: Vec<String> = Vec::new(); 208 let mut v_mod_ids: Vec<String> = Vec::new();
143 for ver in v_versions.clone() { 209 for ver in v_versions.clone() {
144 v_mod_ids.push(ver.project_id); 210 v_mod_ids.push(ver.project_id);
145 }; 211 }
146 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;
147 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));
148 v_projects.sort_by(|a, b| a.id.cmp(&b.id)); 214 v_projects.sort_by(|a, b| a.id.cmp(&b.id));
@@ -150,35 +216,54 @@ async fn get_ver_info(config: Cfg, ver_ids: Vec<String>) -> MLE<Vec<ProjectInfo>
150 for (i, project) in v_projects.into_iter().enumerate() { 216 for (i, project) in v_projects.into_iter().enumerate() {
151 let version = &v_versions[i]; 217 let version = &v_versions[i];
152 println!("\t└{}({})", project.title, version.id); 218 println!("\t└{}({})", project.title, version.id);
153 let file = version.clone().files.into_iter().find(|f| f.primary).unwrap().url; 219 let file = version
154 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()
155 }; 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 }
156 Ok(projectinfo) 235 Ok(projectinfo)
157} 236}
158 237
159fn remove(config: Cfg, input: Input) -> MLE<()> { 238/// Remove mod from a list
160 239/// # Arguments
161 let id = match input.clone().mod_id.unwrap() { 240///
162 IDSelector::ModificationID(id) => id, 241/// * `config` - config struct
163 IDSelector::VersionID(..) => return Err(MLError::new(ErrorType::ArgumentError, "NO_MOD_ID")), 242/// * `id` - name, slug or id of the mod
164 }; 243/// * `list` - List struct
165 244pub fn mod_remove(config: Cfg, id: &str, list: List) -> MLE<()> {
166 let mod_id = mods_get_id(&config.data, &id)?; 245 let mod_id = mods_get_id(&config.data, id)?;
167 246
168 let version = userlist_get_current_version(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?; 247 let version = userlist_get_current_version(config.clone(), &list.id, &mod_id)?;
169 248
170 userlist_remove(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?; 249 userlist_remove(config.clone(), &list.id, &mod_id)?;
171 delete_version(input.list.unwrap(), version)?; 250 delete_version(list, version)?;
172 251
173 let list_ids = lists_get_all_ids(config.clone())?; 252 let list_ids = lists_get_all_ids(config.clone())?;
174 253
254 // Remove mod from main list if not used elsewhere
175 let mut mod_used = false; 255 let mut mod_used = false;
176 for id in list_ids { 256 for id in list_ids {
177 let mods = userlist_get_all_ids(config.clone(), id)?; 257 let mods = userlist_get_all_ids(config.clone(), id)?;
178 if mods.contains(&mod_id) { mod_used = true; break; }; 258 if mods.contains(&mod_id) {
179 }; 259 mod_used = true;
260 break;
261 };
262 }
180 263
181 if !mod_used { mods_remove(config, mod_id)?; }; 264 if !mod_used {
265 mods_remove(config, mod_id)?;
266 };
182 267
183 Ok(()) 268 Ok(())
184} 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 75bee39..3d9578b 100644
--- a/src/commands/update.rs
+++ b/src/commands/update.rs
@@ -1,29 +1,30 @@
1use crate::{config::Cfg, modrinth::{versions, extract_current_version, Version}, get_current_list, db::{userlist_get_all_ids, userlist_get_applicable_versions, userlist_change_versions, lists_get_all_ids, lists_get, userlist_get_current_version, userlist_get_set_version, mods_get_info}, List, input::Input, 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, input: Input) -> MLE<()> { 3 db::{
4 let mut liststack: Vec<List> = vec![]; 4 mods_get_info, userlist_change_versions, userlist_get_all_ids,
5 if input.all_lists { 5 userlist_get_applicable_versions, userlist_get_current_version, userlist_get_set_version,
6 let list_ids = lists_get_all_ids(config.clone())?; 6 },
7 for id in list_ids { 7 error::{ErrorType, MLError, MLE},
8 liststack.push(lists_get(config.clone(), id)?); 8 files::{clean_list_dir, delete_version, disable_version, download_versions},
9 } 9 modrinth::{extract_current_version, versions, Version},
10 } else { 10 List,
11 let current = get_current_list(config.clone())?; 11};
12 println!("Update list {}:", current.id); 12
13 liststack.push(current) 13pub async fn update(
14 } 14 config: Cfg,
15 cmd_update(config, liststack, input.clean, input.direct_download, input.delete_old).await 15 liststack: Vec<List>,
16} 16 clean: bool,
17 17 direct_download: bool,
18pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_download: bool, delete_old: bool) -> MLE<()> { 18 delete_old: bool,
19) -> MLE<()> {
19 for current_list in liststack { 20 for current_list in liststack {
20 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)?;
21 22
22 let mut current_versions: Vec<(String, String)> = vec![]; 23 let mut current_versions: Vec<(String, String)> = vec![];
23 24
24 println!(" └Update mods:"); 25 println!(" └Update mods:");
25 let mut updatestack: Vec<Version> = vec![]; 26 let mut updatestack: Vec<Version> = vec![];
26 27
27 for id in mods { 28 for id in mods {
28 let info = mods_get_info(config.clone(), &id)?; 29 let info = mods_get_info(config.clone(), &id)?;
29 println!("\t└{}", info.title); 30 println!("\t└{}", info.title);
@@ -34,27 +35,39 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
34 } 35 }
35 36
36 //Getting current installed version for disable or delete 37 //Getting current installed version for disable or delete
37 let disable_version = userlist_get_current_version(config.clone(), String::from(&current_list.id), String::from(&id))?; 38 let disable_version =
39 userlist_get_current_version(config.clone(), &current_list.id, &id)?;
38 40
39 updatestack.push( 41 updatestack.push(
40 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 {
41 Ok(ver) => { 50 Ok(ver) => {
42 current_versions.push((disable_version, id)); 51 current_versions.push((disable_version, id));
43 ver 52 ver
44 }, 53 }
45 Err(e) => { 54 Err(e) => {
46 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" { 55 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" {
47 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 );
48 } else { 59 } else {
49 return Err(e); 60 return Err(e);
50 }; 61 };
51 continue; 62 continue;
52 } 63 }
53 } 64 },
54 ) 65 )
55 }; 66 }
56 67
57 if clean { clean_list_dir(&current_list)?; }; 68 if clean {
69 clean_list_dir(&current_list)?;
70 };
58 71
59 if direct_download && !updatestack.is_empty() { 72 if direct_download && !updatestack.is_empty() {
60 download_versions(current_list.clone(), config.clone(), updatestack).await?; 73 download_versions(current_list.clone(), config.clone(), updatestack).await?;
@@ -65,7 +78,7 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
65 if delete_old { 78 if delete_old {
66 println!("\t └Delete version {}", ver.0); 79 println!("\t └Delete version {}", ver.0);
67 delete_version(current_list.clone(), ver.0)?; 80 delete_version(current_list.clone(), ver.0)?;
68 } else if ver.0 != "NONE" { 81 } else if ver.0 != "NONE" {
69 println!("\t └Disable version {}", ver.0); 82 println!("\t └Disable version {}", ver.0);
70 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?; 83 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?;
71 }; 84 };
@@ -78,10 +91,11 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
78} 91}
79 92
80async 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> {
81 let applicable_versions = versions(&config.apis.modrinth, String::from(&id), list.clone()).await; 94 let applicable_versions =
82 95 versions(&config.apis.modrinth, String::from(&id), list.clone()).await;
96
83 let mut versions: Vec<String> = vec![]; 97 let mut versions: Vec<String> = vec![];
84 98
85 if !applicable_versions.is_empty() { 99 if !applicable_versions.is_empty() {
86 for ver in &applicable_versions { 100 for ver in &applicable_versions {
87 versions.push(String::from(&ver.id)); 101 versions.push(String::from(&ver.id));
@@ -92,8 +106,14 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML
92 106
93 let mut current: Vec<Version> = vec![]; 107 let mut current: Vec<Version> = vec![];
94 //TODO Split clean and no match 108 //TODO Split clean and no match
95 if clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&id))?) { 109 if clean
96 110 || (versions.join("|")
111 != userlist_get_applicable_versions(
112 config.clone(),
113 String::from(&list.id),
114 String::from(&id),
115 )?)
116 {
97 let current_str = extract_current_version(applicable_versions.clone())?; 117 let current_str = extract_current_version(applicable_versions.clone())?;
98 118
99 if clean { 119 if clean {
@@ -104,34 +124,54 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML
104 }; 124 };
105 125
106 //get new versions 126 //get new versions
107 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 {
108 Ok(v) => Ok(v), 132 Ok(v) => Ok(v),
109 Err(e) => Err(MLError::new(ErrorType::Other, e)), 133 Err(e) => Err(MLError::new(ErrorType::Other, e)),
110 }?; 134 }?;
111 current.push(current_ver.clone()); 135 current.push(current_ver.clone());
112 136
113 let link = match current_ver.files.into_iter().find(|f| f.primary).ok_or("!no primary in links") { 137 //TODO implement version selection if no primary
138 let link = match current_ver
139 .files
140 .into_iter()
141 .find(|f| f.primary)
142 .ok_or("!no primary in links")
143 {
114 Ok(p) => Ok(p), 144 Ok(p) => Ok(p),
115 Err(e) => Err(MLError::new(ErrorType::Other, e)), 145 Err(e) => Err(MLError::new(ErrorType::Other, e)),
116 }?.url; 146 }?
147 .url;
117 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)?;
118 } 149 }
119 150
120 if current.is_empty() { return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE")) }; 151 if current.is_empty() {
121 152 return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE"));
153 };
154
122 //println!(" └✔️"); 155 //println!(" └✔️");
123 Ok(current[0].clone()) 156 Ok(current[0].clone())
124} 157}
125 158
126#[tokio::test] 159#[tokio::test]
127async fn download_updates_test() { 160async fn download_updates_test() {
161 use crate::{
162 modrinth::{Hash, Version, VersionFile, VersionType},
163 List, Modloader,
164 };
128 165
129 use crate::{modrinth::{Version, VersionFile, Hash, VersionType}, Modloader, List};
130
131 let config = Cfg::init("modlist.toml").unwrap(); 166 let config = Cfg::init("modlist.toml").unwrap();
132 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 {
133 168 id: String::from("..."),
134 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 {
135 id: "dEqtGnT9".to_string(), 175 id: "dEqtGnT9".to_string(),
136 project_id: "kYuIpRLv".to_string(), 176 project_id: "kYuIpRLv".to_string(),
137 author_id: "Qnt13hO8".to_string(), 177 author_id: "Qnt13hO8".to_string(),
@@ -161,5 +201,7 @@ async fn download_updates_test() {
161 "fabric".to_string() 201 "fabric".to_string()
162 ] 202 ]
163 }]; 203 }];
164 assert!(download_versions(current_list, config, versions).await.is_ok()) 204 assert!(download_versions(current_list, config, versions)
205 .await
206 .is_ok())
165} 207}
diff --git a/src/config.rs b/src/config.rs
index ded0062..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,10 +27,15 @@ 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())?;
31 File::open(devdir(configfile.to_str().unwrap()))? 39 File::open(devdir(configfile.to_str().unwrap()))?
32 } else { 40 } else {
33 return Err(err.into()); 41 return Err(err.into());
diff --git a/src/db.rs b/src/db.rs
index 9428466..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,33 +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> {
54 //TODO check if "slug" is id
55
52 let data = devdir(format!("{}/data.db", data).as_str()); 56 let data = devdir(format!("{}/data.db", data).as_str());
53 let connection = Connection::open(data)?; 57 let connection = Connection::open(data)?;
54 58
55 let mut mod_id = String::new(); 59 let mut mod_id = String::new();
56 60
57 //get from slug 61 //get from slug
58 let mut stmt = connection.prepare("SELECT id FROM mods WHERE slug = ?")?; 62 let mut stmt = connection.prepare("SELECT id FROM mods WHERE slug = ?")?;
59 let id_iter = stmt.query_map([slug], |row| { 63 let id_iter = stmt.query_map([slug], |row| row.get::<usize, String>(0))?;
60 row.get::<usize, String>(0)
61 })?;
62 64
63 for id in id_iter { 65 for id in id_iter {
64 mod_id = id?; 66 mod_id = id?;
65 }; 67 }
66 //get from title if no id found from slug 68 //get from title if no id found from slug
67 if mod_id.is_empty() { 69 if mod_id.is_empty() {
68 let mut stmt = connection.prepare("SELECT id FROM mods WHERE title = ?")?; 70 let mut stmt = connection.prepare("SELECT id FROM mods WHERE title = ?")?;
69 let id_iter = stmt.query_map([slug], |row| { 71 let id_iter = stmt.query_map([slug], |row| row.get::<usize, String>(0))?;
70 row.get::<usize, String>(0)
71 })?;
72 72
73 for id in id_iter { 73 for id in id_iter {
74 mod_id = id?; 74 mod_id = id?;
75 }; 75 }
76 } 76 }
77 77
78 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 };
79 81
80 Ok(mod_id) 82 Ok(mod_id)
81} 83}
@@ -88,17 +90,23 @@ pub struct ModInfo {
88pub fn mods_get_info(config: Cfg, id: &str) -> MLE<ModInfo> { 90pub fn mods_get_info(config: Cfg, id: &str) -> MLE<ModInfo> {
89 let data = devdir(format!("{}/data.db", config.data).as_str()); 91 let data = devdir(format!("{}/data.db", config.data).as_str());
90 let connection = Connection::open(data)?; 92 let connection = Connection::open(data)?;
91 93
92 let mut mod_info: Option<ModInfo> = None; 94 let mut mod_info: Option<ModInfo> = None;
93 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 = ?")?;
94 let name_iter = stmt.query_map([id], |row| { 96 let name_iter = stmt.query_map([id], |row| {
95 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 ])
96 })?; 101 })?;
97 102
98 for info in name_iter { 103 for info in name_iter {
99 let i = info?; 104 let i = info?;
100 mod_info = Some(ModInfo { title: String::from(&i[0]), slug: String::from(&i[1]) }); 105 mod_info = Some(ModInfo {
101 }; 106 title: String::from(&i[0]),
107 slug: String::from(&i[1]),
108 });
109 }
102 110
103 match mod_info.is_none() { 111 match mod_info.is_none() {
104 true => Err(MLError::new(ErrorType::DBError, "GN_MOD_NOT_FOUND")), 112 true => Err(MLError::new(ErrorType::DBError, "GN_MOD_NOT_FOUND")),
@@ -107,7 +115,6 @@ pub fn mods_get_info(config: Cfg, id: &str) -> MLE<ModInfo> {
107} 115}
108 116
109pub fn mods_remove(config: Cfg, id: String) -> MLE<()> { 117pub fn mods_remove(config: Cfg, id: String) -> MLE<()> {
110
111 println!("Removing mod {} from database", id); 118 println!("Removing mod {} from database", id);
112 119
113 let data = devdir(format!("{}/data.db", config.data).as_str()); 120 let data = devdir(format!("{}/data.db", config.data).as_str());
@@ -128,27 +135,42 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
128 let data = devdir(format!("{}/data.db", config.data).as_str()); 135 let data = devdir(format!("{}/data.db", config.data).as_str());
129 let connection = Connection::open(data)?; 136 let connection = Connection::open(data)?;
130 137
131 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 }
132 141
133 let mut wherestr = String::from("WHERE"); 142 let mut wherestr = String::from("WHERE");
134 for (i, id) in mods.iter().enumerate() { 143 for (i, id) in mods.iter().enumerate() {
135 let mut or = " OR"; 144 let mut or = " OR";
136 if i == mods.len() - 1 { or = "" }; 145 if i == mods.len() - 1 {
146 or = ""
147 };
137 wherestr = format!("{} id = '{}'{}", wherestr, id, or); 148 wherestr = format!("{} id = '{}'{}", wherestr, id, or);
138 } 149 }
139 150
140 let mut versionmaps: Vec<DBModlistVersions> = Vec::new(); 151 let mut versionmaps: Vec<DBModlistVersions> = Vec::new();
141 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())?;
142 let id_iter = stmt.query_map([], |row| { 154 let id_iter = stmt.query_map([], |row| {
143 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 ])
144 })?; 160 })?;
145 161
146 for ver in id_iter { 162 for ver in id_iter {
147 let version = ver?; 163 let version = ver?;
148 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 );
149 //println!("Found versions {} for mod {}", version[1], version[0]); 168 //println!("Found versions {} for mod {}", version[1], version[0]);
150 versionmaps.push(DBModlistVersions { mod_id: String::from(&version[0]), versions: String::from(&version[1]) }) 169 versionmaps.push(DBModlistVersions {
151 }; 170 mod_id: String::from(&version[0]),
171 versions: String::from(&version[1]),
172 })
173 }
152 174
153 match versionmaps.is_empty() { 175 match versionmaps.is_empty() {
154 true => Err(MLError::new(ErrorType::DBError, "MODS_MODS_NOT_FOUND")), 176 true => Err(MLError::new(ErrorType::DBError, "MODS_MODS_NOT_FOUND")),
@@ -157,16 +179,37 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
157} 179}
158 180
159//userlist 181//userlist
160pub 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<()> {
161 let data = devdir(format!("{}/data.db", config.data).as_str()); 191 let data = devdir(format!("{}/data.db", config.data).as_str());
162 let connection = Connection::open(data)?; 192 let connection = Connection::open(data)?;
163 193
164 let sv = match set_version { 194 let sv = match set_version {
165 true => "1", 195 true => "1",
166 false => "0", 196 false => "0",
167 }; 197 };
168 198
169 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 )?;
170 213
171 Ok(()) 214 Ok(())
172} 215}
@@ -177,14 +220,12 @@ pub fn userlist_get_all_ids(config: Cfg, list_id: String) -> MLE<Vec<String>> {
177 220
178 let mut mod_ids: Vec<String> = Vec::new(); 221 let mut mod_ids: Vec<String> = Vec::new();
179 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())?;
180 let id_iter = stmt.query_map([], |row| { 223 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
181 row.get::<usize, String>(0)
182 })?;
183 224
184 for id in id_iter { 225 for id in id_iter {
185 //println!("Found id {:?}", id.as_ref().unwrap()); 226 //println!("Found id {:?}", id.as_ref().unwrap());
186 mod_ids.push(id?) 227 mod_ids.push(id?)
187 }; 228 }
188 229
189 match mod_ids.is_empty() { 230 match mod_ids.is_empty() {
190 true => Err(MLError::new(ErrorType::DBError, "NO_MODS")), 231 true => Err(MLError::new(ErrorType::DBError, "NO_MODS")),
@@ -192,29 +233,38 @@ pub fn userlist_get_all_ids(config: Cfg, list_id: String) -> MLE<Vec<String>> {
192 } 233 }
193} 234}
194 235
195 236pub fn userlist_remove(config: Cfg, list_id: &str, mod_id: &str) -> MLE<()> {
196pub fn userlist_remove(config: Cfg, list_id: String, mod_id: String) -> MLE<()> {
197 let data = devdir(format!("{}/data.db", config.data).as_str()); 237 let data = devdir(format!("{}/data.db", config.data).as_str());
198 let connection = Connection::open(data)?; 238 let connection = Connection::open(data)?;
199 239
200 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 )?;
201 Ok(()) 244 Ok(())
202} 245}
203 246
204 247pub fn userlist_get_applicable_versions(
205pub 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> {
206 let data = devdir(format!("{}/data.db", config.data).as_str()); 252 let data = devdir(format!("{}/data.db", config.data).as_str());
207 let connection = Connection::open(data).unwrap(); 253 let connection = Connection::open(data).unwrap();
208 254
209 let mut version: String = String::new(); 255 let mut version: String = String::new();
210 let mut stmt = connection.prepare(format!("SELECT applicable_versions FROM {} WHERE mod_id = ?", list_id).as_str())?; 256 let mut stmt = connection.prepare(
211 let ver_iter = stmt.query_map([mod_id], |row| { 257 format!(
212 row.get::<usize, String>(0) 258 "SELECT applicable_versions FROM {} WHERE mod_id = ?",
213 })?; 259 list_id
260 )
261 .as_str(),
262 )?;
263 let ver_iter = stmt.query_map([mod_id], |row| row.get::<usize, String>(0))?;
214 264
215 for ver in ver_iter { 265 for ver in ver_iter {
216 version = ver?; 266 version = ver?;
217 }; 267 }
218 268
219 match version.is_empty() { 269 match version.is_empty() {
220 true => Err(MLError::new(ErrorType::DBError, "GAV_MOD_NOT_FOUND")), 270 true => Err(MLError::new(ErrorType::DBError, "GAV_MOD_NOT_FOUND")),
@@ -222,39 +272,47 @@ pub fn userlist_get_applicable_versions(config: Cfg, list_id: String, mod_id: St
222 } 272 }
223} 273}
224 274
225pub 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)>> {
226 let data = devdir(format!("{}/data.db", config.data).as_str()); 279 let data = devdir(format!("{}/data.db", config.data).as_str());
227 let connection = Connection::open(data)?; 280 let connection = Connection::open(data)?;
228 281
229 let mut versions: Vec<(String, String)> = Vec::new(); 282 let mut versions: Vec<(String, String)> = Vec::new();
230 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())?;
231 let id_iter = stmt.query_map([], |row| { 285 let id_iter = stmt.query_map([], |row| {
232 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 ])
233 })?; 290 })?;
234 291
235 for ver in id_iter { 292 for ver in id_iter {
236 let out = ver?; 293 let out = ver?;
237 versions.push((out[0].to_owned(), out[1].to_owned())); 294 versions.push((out[0].to_owned(), out[1].to_owned()));
238 }; 295 }
239 296
240 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 };
241 300
242 Ok(versions) 301 Ok(versions)
243} 302}
244 303
245pub fn userlist_get_current_version(config: Cfg, list_id: String, mod_id: String) -> MLE<String> { 304pub fn userlist_get_current_version(config: Cfg, list_id: &str, mod_id: &str) -> MLE<String> {
246 let data = devdir(format!("{}/data.db", config.data).as_str()); 305 let data = devdir(format!("{}/data.db", config.data).as_str());
247 let connection = Connection::open(data).unwrap(); 306 let connection = Connection::open(data).unwrap();
248 307
249 let mut version: String = String::new(); 308 let mut version: String = String::new();
250 let mut stmt = connection.prepare(format!("SELECT current_version FROM {} WHERE mod_id = ?", list_id).as_str())?; 309 let mut stmt = connection
251 let ver_iter = stmt.query_map([&mod_id], |row| { 310 .prepare(format!("SELECT current_version FROM {} WHERE mod_id = ?", list_id).as_str())?;
252 row.get::<usize, String>(0) 311 let ver_iter = stmt.query_map([&mod_id], |row| row.get::<usize, String>(0))?;
253 })?;
254 312
255 for ver in ver_iter { 313 for ver in ver_iter {
256 version = ver?; 314 version = ver?;
257 }; 315 }
258 316
259 match version.is_empty() { 317 match version.is_empty() {
260 true => Err(MLError::new(ErrorType::DBError, "GCV_MOD_NOT_FOUND")), 318 true => Err(MLError::new(ErrorType::DBError, "GCV_MOD_NOT_FOUND")),
@@ -262,63 +320,88 @@ pub fn userlist_get_current_version(config: Cfg, list_id: String, mod_id: String
262 } 320 }
263} 321}
264 322
265pub 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>> {
266 let data = devdir(format!("{}/data.db", config.data).as_str()); 327 let data = devdir(format!("{}/data.db", config.data).as_str());
267 let connection = Connection::open(data)?; 328 let connection = Connection::open(data)?;
268 329
269 let mut versions: Vec<String> = Vec::new(); 330 let mut versions: Vec<String> = Vec::new();
270 let mut stmt = connection.prepare(format!("SELECT current_version FROM {}", list_id).as_str())?; 331 let mut stmt =
271 let id_iter = stmt.query_map([], |row| { 332 connection.prepare(format!("SELECT current_version FROM {}", list_id).as_str())?;
272 row.get::<usize, String>(0) 333 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
273 })?;
274 334
275 for id in id_iter { 335 for id in id_iter {
276 versions.push(id?); 336 versions.push(id?);
277 }; 337 }
278 338
279 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 };
280 345
281 Ok(versions) 346 Ok(versions)
282} 347}
283 348
284pub 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>> {
285 let data = devdir(format!("{}/data.db", config.data).as_str()); 353 let data = devdir(format!("{}/data.db", config.data).as_str());
286 let connection = Connection::open(data)?; 354 let connection = Connection::open(data)?;
287 355
288 let mut versions: Vec<(String, String)> = Vec::new(); 356 let mut versions: Vec<(String, String)> = Vec::new();
289 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())?;
290 let id_iter = stmt.query_map([], |row| { 359 let id_iter = stmt.query_map([], |row| {
291 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 ])
292 })?; 364 })?;
293 365
294 for ver in id_iter { 366 for ver in id_iter {
295 let out = ver?; 367 let out = ver?;
296 versions.push((out[0].to_owned(), out[1].to_owned())); 368 versions.push((out[0].to_owned(), out[1].to_owned()));
297 }; 369 }
298 370
299 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 };
300 377
301 Ok(versions) 378 Ok(versions)
302} 379}
303 380
304pub 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> {
305 let data = devdir(format!("{}/data.db", config.data).as_str()); 382 let data = devdir(format!("{}/data.db", config.data).as_str());
306 let connection = Connection::open(data).unwrap(); 383 let connection = Connection::open(data).unwrap();
307 384
308 let mut set_version: bool = false; 385 let mut set_version: bool = false;
309 let mut stmt = connection.prepare(format!("SELECT set_version FROM {} WHERE mod_id = ?", list_id).as_str())?; 386 let mut stmt = connection
310 let ver_iter = stmt.query_map([&mod_id], |row| { 387 .prepare(format!("SELECT set_version FROM {} WHERE mod_id = ?", list_id).as_str())?;
311 row.get::<usize, bool>(0) 388 let ver_iter = stmt.query_map([&mod_id], |row| row.get::<usize, bool>(0))?;
312 })?;
313 389
314 for ver in ver_iter { 390 for ver in ver_iter {
315 set_version = ver?; 391 set_version = ver?;
316 }; 392 }
317 393
318 Ok(set_version) 394 Ok(set_version)
319} 395}
320 396
321pub 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<()> {
322 let data = devdir(format!("{}/data.db", config.data).as_str()); 405 let data = devdir(format!("{}/data.db", config.data).as_str());
323 let connection = Connection::open(data)?; 406 let connection = Connection::open(data)?;
324 407
@@ -326,33 +409,45 @@ pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: S
326 Ok(()) 409 Ok(())
327} 410}
328 411
329pub 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<()> {
330 let data = devdir(format!("{}/data.db", config.data).as_str()); 418 let data = devdir(format!("{}/data.db", config.data).as_str());
331 let connection = Connection::open(data)?; 419 let connection = Connection::open(data)?;
332 420
333 let currently_disabled_versions = userlist_get_disabled_versions(config, String::from(&list_id), String::from(&mod_id))?; 421 let currently_disabled_versions =
334 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" {
335 true => disabled_version, 424 true => disabled_version,
336 false => format!("{}|{}", currently_disabled_versions, disabled_version), 425 false => format!("{}|{}", currently_disabled_versions, disabled_version),
337 }; 426 };
338 427
339 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 )?;
340 Ok(()) 436 Ok(())
341} 437}
342 438
343pub 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> {
344 let data = devdir(format!("{}/data.db", config.data).as_str()); 440 let data = devdir(format!("{}/data.db", config.data).as_str());
345 let connection = Connection::open(data).unwrap(); 441 let connection = Connection::open(data).unwrap();
346 442
347 let mut version: String = String::new(); 443 let mut version: String = String::new();
348 let mut stmt = connection.prepare(format!("SELECT disabled_versions FROM {} WHERE mod_id = ?", list_id).as_str())?; 444 let mut stmt = connection
349 let ver_iter = stmt.query_map([mod_id], |row| { 445 .prepare(format!("SELECT disabled_versions FROM {} WHERE mod_id = ?", list_id).as_str())?;
350 row.get::<usize, String>(0) 446 let ver_iter = stmt.query_map([mod_id], |row| row.get::<usize, String>(0))?;
351 })?;
352 447
353 for ver in ver_iter { 448 for ver in ver_iter {
354 version = ver?; 449 version = ver?;
355 }; 450 }
356 451
357 match version.is_empty() { 452 match version.is_empty() {
358 true => Err(MLError::new(ErrorType::DBError, "GDV_MOD_NOT_FOUND")), 453 true => Err(MLError::new(ErrorType::DBError, "GDV_MOD_NOT_FOUND")),
@@ -360,36 +455,57 @@ pub fn userlist_get_disabled_versions(config:Cfg, list_id: String, mod_id: Strin
360 } 455 }
361} 456}
362 457
363pub 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>> {
364 let data = devdir(format!("{}/data.db", config.data).as_str()); 462 let data = devdir(format!("{}/data.db", config.data).as_str());
365 let connection = Connection::open(data).unwrap(); 463 let connection = Connection::open(data).unwrap();
366 464
367 let mut links: Vec<String> = Vec::new(); 465 let mut links: Vec<String> = Vec::new();
368 let mut stmt = connection.prepare(format!("SELECT current_download FROM {}", list_id).as_str())?; 466 let mut stmt =
369 let link_iter = stmt.query_map([], |row| { 467 connection.prepare(format!("SELECT current_download FROM {}", list_id).as_str())?;
370 row.get::<usize, String>(0) 468 let link_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
371 })?;
372 469
373 for link in link_iter { 470 for link in link_iter {
374 let l = link?; 471 let l = link?;
375 println!("Found link {}", String::from(&l)); 472 println!("Found link {}", String::from(&l));
376 links.push(l) 473 links.push(l)
377 }; 474 }
378 475
379 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 };
380 482
381 Ok(links) 483 Ok(links)
382} 484}
383 485
384//lists 486//lists
385///Inserts into lists table and creates new table 487///Inserts into lists table and creates new table
386pub 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<()> {
387 println!("Creating list {}", id); 495 println!("Creating list {}", id);
388 496
389 let data = devdir(format!("{}/data.db", config.data).as_str()); 497 let data = devdir(format!("{}/data.db", config.data).as_str());
390 let connection = Connection::open(data)?; 498 let connection = Connection::open(data)?;
391 499
392 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 )?;
393 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(), [])?;
394 510
395 Ok(()) 511 Ok(())
@@ -408,44 +524,62 @@ pub fn lists_get(config: Cfg, list_id: String) -> MLE<List> {
408 let data = devdir(format!("{}/data.db", config.data).as_str()); 524 let data = devdir(format!("{}/data.db", config.data).as_str());
409 let connection = Connection::open(data).unwrap(); 525 let connection = Connection::open(data).unwrap();
410 526
411 let mut list = List { id: String::new(), mc_version: String::new(), modloader: Modloader::Fabric, download_folder: String::new() }; 527 let mut list = List {
412 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 = ?")?;
413 535
414 let list_iter = stmt.query_map([&list_id], |row| { 536 let list_iter = stmt.query_map([&list_id], |row| {
415 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 ])
416 })?; 542 })?;
417 543
418 for l in list_iter { 544 for l in list_iter {
419 let li = l?; 545 let li = l?;
420 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 {
421 }; 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 }
422 557
423 if list.id.is_empty() { return Err(MLError::new(ErrorType::DBError, "LIST_NOT_FOUND")); }
424
425 Ok(list) 558 Ok(list)
426} 559}
427 560
428pub fn lists_version(config: Cfg, list_id: String, version: String) -> MLE<()> { 561pub fn lists_version(config: Cfg, list_id: &str, version: &str) -> MLE<()> {
429 let data = devdir(format!("{}/data.db", config.data).as_str()); 562 let data = devdir(format!("{}/data.db", config.data).as_str());
430 let connection = Connection::open(data).unwrap(); 563 let connection = Connection::open(data).unwrap();
431 564
432 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 )?;
433 Ok(()) 569 Ok(())
434} 570}
435 571
436pub fn lists_get_all_ids(config: Cfg) -> MLE<Vec<String>> { 572pub fn lists_get_all_ids(config: Cfg) -> MLE<Vec<String>> {
437 let data = devdir(format!("{}/data.db", config.data).as_str()); 573 let data = devdir(format!("{}/data.db", config.data).as_str());
438 let connection = Connection::open(data).unwrap(); 574 let connection = Connection::open(data).unwrap();
439 575
440 let mut list_ids: Vec<String> = Vec::new(); 576 let mut list_ids: Vec<String> = Vec::new();
441 let mut stmt = connection.prepare("SELECT id FROM lists")?; 577 let mut stmt = connection.prepare("SELECT id FROM lists")?;
442 let id_iter = stmt.query_map([], |row| { 578 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
443 row.get::<usize, String>(0)
444 })?;
445 579
446 for id in id_iter { 580 for id in id_iter {
447 list_ids.push(id?) 581 list_ids.push(id?)
448 }; 582 }
449 583
450 match list_ids.is_empty() { 584 match list_ids.is_empty() {
451 true => Err(MLError::new(ErrorType::DBError, "NO_LISTS")), 585 true => Err(MLError::new(ErrorType::DBError, "NO_LISTS")),
@@ -458,35 +592,50 @@ pub fn config_change_current_list(config: Cfg, id: String) -> MLE<()> {
458 let data = devdir(format!("{}/data.db", config.data).as_str()); 592 let data = devdir(format!("{}/data.db", config.data).as_str());
459 let connection = Connection::open(data)?; 593 let connection = Connection::open(data)?;
460 594
461 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 )?;
462 Ok(()) 599 Ok(())
463} 600}
464 601
465pub fn config_get_current_list(config: Cfg) -> MLE<String> { 602pub fn config_get_current_list(config: Cfg) -> MLE<String> {
466 let data = devdir(format!("{}/data.db", config.data).as_str()); 603 let data = devdir(format!("{}/data.db", config.data).as_str());
467 let connection = Connection::open(data).unwrap(); 604 let connection = Connection::open(data).unwrap();
468 605
469 let mut list_id = String::new(); 606 let mut list_id = String::new();
470 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'")?;
471 let list_iter = stmt.query_map([], |row| { 608 let list_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
472 row.get::<usize, String>(0)
473 })?;
474 609
475 for list in list_iter { 610 for list in list_iter {
476 list_id = list?; 611 list_id = list?;
477 }; 612 }
613
614 if list_id.is_empty() {
615 return Err(MLError::new(ErrorType::DBError, "NO_CURRENT_LIST"));
616 }
478 617
479 if list_id.is_empty() { return Err(MLError::new(ErrorType::DBError, "NO_CURRENT_LIST")); }
480
481 Ok(list_id) 618 Ok(list_id)
482} 619}
483 620
484//SETUP(UPDATES) 621//SETUP(UPDATES)
485pub 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>> {
486 let data = devdir(format!("{}/data.db", config.data).as_str()); 628 let data = devdir(format!("{}/data.db", config.data).as_str());
487 let connection = Connection::open(data)?; 629 let connection = Connection::open(data)?;
488 630
489 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 )?;
490 Ok(()) 639 Ok(())
491} 640}
492 641
@@ -494,7 +643,10 @@ pub fn s_config_create_version(config: Cfg) -> Result<(), Box<dyn std::error::Er
494 let data = devdir(format!("{}/data.db", config.data).as_str()); 643 let data = devdir(format!("{}/data.db", config.data).as_str());
495 let connection = Connection::open(data)?; 644 let connection = Connection::open(data)?;
496 645
497 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 )?;
498 Ok(()) 650 Ok(())
499} 651}
500 652
@@ -502,34 +654,46 @@ pub fn s_config_update_version(config: Cfg, ver: String) -> Result<(), Box<dyn s
502 let data = devdir(format!("{}/data.db", config.data).as_str()); 654 let data = devdir(format!("{}/data.db", config.data).as_str());
503 let connection = Connection::open(data)?; 655 let connection = Connection::open(data)?;
504 656
505 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 )?;
506 Ok(()) 661 Ok(())
507} 662}
508 663
509pub 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>> {
510 let data = devdir(format!("{}/data.db", config.data).as_str()); 665 let data = devdir(format!("{}/data.db", config.data).as_str());
511 let connection = Connection::open(data)?; 666 let connection = Connection::open(data)?;
512 667
513 let mut version: String = String::new(); 668 let mut version: String = String::new();
514 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'")?;
515 let ver_iter = stmt.query_map([], |row| { 670 let ver_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
516 row.get::<usize, String>(0)
517 })?;
518 671
519 for ver in ver_iter { 672 for ver in ver_iter {
520 version = ver?; 673 version = ver?;
521 }; 674 }
522 675
523 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 };
524 Ok(version) 682 Ok(version)
525} 683}
526 684
527pub 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>> {
528 let data = devdir(format!("{}/data.db", config.data).as_str()); 692 let data = devdir(format!("{}/data.db", config.data).as_str());
529 let connection = Connection::open(data)?; 693 let connection = Connection::open(data)?;
530 694
531 let mut sql = format!("ALTER TABLE {} ADD '{}' {}", table, column, c_type); 695 let mut sql = format!("ALTER TABLE {} ADD '{}' {}", table, column, c_type);
532 696
533 if default.is_some() { 697 if default.is_some() {
534 sql = format!("{} DEFAULT {}", sql, default.unwrap()); 698 sql = format!("{} DEFAULT {}", sql, default.unwrap());
535 } 699 }
@@ -539,7 +703,6 @@ pub fn s_insert_column(config: Cfg, table: String, column: String, c_type: Strin
539} 703}
540 704
541pub fn db_setup(config: Cfg) -> MLE<()> { 705pub fn db_setup(config: Cfg) -> MLE<()> {
542
543 println!("Initiating database"); 706 println!("Initiating database");
544 707
545 let data = devdir(format!("{}/data.db", config.data).as_str()); 708 let data = devdir(format!("{}/data.db", config.data).as_str());
@@ -552,6 +715,6 @@ pub fn db_setup(config: Cfg) -> MLE<()> {
552 INSERT INTO 'user_config' VALUES ( 'db_version', '0.5' ); 715 INSERT INTO 'user_config' VALUES ( 'db_version', '0.5' );
553 INSERT INTO 'user_config' VALUES ( 'current_list', '...' )", 716 INSERT INTO 'user_config' VALUES ( 'current_list', '...' )",
554 )?; 717 )?;
555 718
556 Ok(()) 719 Ok(())
557} 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/input.rs b/src/input.rs
deleted file mode 100644
index 6c62ab7..0000000
--- a/src/input.rs
+++ /dev/null
@@ -1,344 +0,0 @@
1use crate::{error::{MLE, MLError, ErrorType}, Modloader, config::Cfg, db::lists_get, get_current_list, List, modrinth::{get_minecraft_version, MCVersionType}, IDSelector};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct Input {
5 pub command: Option<Cmd>,
6 pub mod_options: Option<ModOptions>,
7 pub mod_id: Option<IDSelector>,
8 pub set_version: bool,
9 pub all_lists: bool,
10 pub clean: bool,
11 pub direct_download: bool,
12 pub delete_old: bool,
13 pub list: Option<List>,
14 pub list_options: Option<ListOptions>,
15 pub list_id: Option<String>,
16 pub list_mcversion: Option<String>,
17 pub modloader: Option<Modloader>,
18 pub directory: Option<String>,
19 pub io_options: Option<IoOptions>,
20 pub file: Option<String>,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum Cmd {
25 Mod,
26 List,
27 Update,
28 Download,
29 Io,
30 Version,
31 Setup,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum ModOptions {
36 Add,
37 Remove
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum ListOptions {
42 Add,
43 Remove,
44 Change,
45 Version,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum IoOptions {
50 Export,
51 Import
52}
53
54impl Input {
55 fn from(config: Cfg, input: Vec<String>) -> MLE<Self> {
56 let input_string = input.join(" ");
57 let mut args: Vec<&str> = input_string.split(" -").collect();
58 args[0] = args[0].split_at(1).1;
59
60 let mut command: Option<Cmd> = None;
61
62 let mut mod_options: Option<ModOptions> = None;
63 let mut mod_id: Option<IDSelector> = None;
64 let mut set_version = false;
65 let mut all_lists = false;
66 let mut clean = false;
67 let mut direct_download = true;
68 let mut delete_old = false;
69 let mut list: Option<List> = None;
70 let mut list_options: Option<ListOptions> = None;
71 let mut list_id: Option<String> = None;
72 let mut list_mcversion: Option<String> = None;
73 let mut modloader: Option<Modloader> = None;
74 let mut directory: Option<String> = None;
75 let mut io_options: Option<IoOptions> = None;
76 let mut file: Option<String> = None;
77
78 for arg in args {
79 let arg_split: Vec<&str> = arg.trim().split(' ').collect();
80 match arg_split[0] {
81 "v" | "version" => {
82 command = Some(Cmd::Version);
83 },
84 "d" | "download" => {
85 command = Some(Cmd::Download);
86 },
87 "u" | "update" => {
88 command = Some(Cmd::Update);
89 },
90 "ma" => {
91 command = Some(Cmd::Mod);
92 mod_options = Some(ModOptions::Add);
93 if arg_split.len() == 2 {
94 mod_id = Some(IDSelector::ModificationID(String::from(arg_split[1])));
95 } else {
96 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a list mod slug or id"));
97 }
98 },
99 //TODO impl this
100 "mv" => {
101 command = Some(Cmd::Mod);
102 mod_options = Some(ModOptions::Add);
103 if arg_split.len() == 2 {
104 mod_id = Some(IDSelector::VersionID(String::from(arg_split[1])));
105 } else {
106 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a version id"));
107 };
108 },
109 "mr" => {
110 command = Some(Cmd::Mod);
111 mod_options = Some(ModOptions::Remove);
112 if arg_split.len() == 2 {
113 mod_id = Some(IDSelector::ModificationID(String::from(arg_split[1])));
114 } else {
115 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a mod id"));
116 };
117 },
118 "set_version" => {
119 set_version = true;
120 },
121 "all_lists" => {
122 all_lists = true;
123 },
124 "clean" => {
125 clean = true;
126 },
127 "no_download" => {
128 direct_download = false;
129 },
130 "delete_old" => {
131 delete_old = true;
132 },
133 "l" => {
134 if arg_split.len() == 2 {
135 list = Some(lists_get(config.clone(), String::from(arg_split[1]))?);
136 } else {
137 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a list via it's id"));
138 }
139 }
140 "la" => {
141 command = Some(Cmd::List);
142 list_options = Some(ListOptions::Add);
143 if arg_split.len() == 2 {
144 list_id = Some(String::from(arg_split[1]));
145 } else {
146 return Err(MLError::new(ErrorType::ArgumentError, "Please give the new list an id"));
147 }
148 },
149 "lr" => {
150 command = Some(Cmd::List);
151 list_options = Some(ListOptions::Remove);
152 },
153 "lc" => {
154 command = Some(Cmd::List);
155 list_options = Some(ListOptions::Change);
156 },
157 "lv" => {
158 command = Some(Cmd::List);
159 list_options = Some(ListOptions::Version);
160 if arg_split.len() == 2 {
161 list_mcversion = Some(String::from(arg_split[1]));
162 } else {
163 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a minecraft version"));
164 }
165 },
166 "mcv" => {
167 if arg_split.len() == 2 {
168 list_mcversion = Some(String::from(arg_split[1]));
169 } else {
170 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a minecraft version"));
171 }
172 },
173 "ml" => {
174 if arg_split.len() == 2 {
175 modloader = Some(Modloader::from(arg_split[1])?);
176 } else {
177 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a modloader"));
178 }
179 },
180 "dir" => {
181 if arg_split.len() == 2 {
182 directory = Some(String::from(arg_split[1]));
183 } else {
184 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a directory"));
185 }
186 },
187 "export" => {
188 command = Some(Cmd::Io);
189 io_options = Some(IoOptions::Export);
190 },
191 "import" => {
192 command = Some(Cmd::Io);
193 io_options = Some(IoOptions::Import);
194 },
195 "f" => {
196 file = Some(String::from(arg_split[1]));
197 },
198 "setup" => {
199 command = Some(Cmd::Setup);
200 }
201 _ => return Err(MLError::new(ErrorType::ArgumentError, format!("Unknown Argument ({})", arg_split[0]).as_str())),
202 }
203 }
204
205 Ok(Self {
206 command,
207 mod_options,
208 mod_id,
209 set_version,
210 all_lists,
211 clean,
212 direct_download,
213 delete_old,
214 list,
215 list_options,
216 list_id,
217 list_mcversion,
218 modloader,
219 directory,
220 io_options,
221 file
222 })
223 }
224}
225
226pub async fn get_input(config: Cfg, args: Vec<String>) -> MLE<Input> {
227 let input = Input::from(config.clone(), args)?;
228
229 if input.command.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "No command specified")); };
230
231 match input.clone().command.unwrap() {
232 Cmd::Mod => check_mod(input, config),
233 Cmd::List => check_list(input, config).await,
234 _ => Ok(input),
235 }
236}
237
238fn check_mod(mut input: Input, config: Cfg) -> MLE<Input> {
239 if input.mod_options.is_none() {
240 return Err(MLError::new(ErrorType::ArgumentError, "No mod option"));
241 };
242 match input.clone().mod_options.unwrap() {
243 ModOptions::Add => {
244 if input.mod_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "No mod id/slug or version id")); };
245 if input.list_id.is_none() { input.list = Some(get_current_list(config)?); };
246 Ok(input)
247 },
248 ModOptions::Remove => {
249 if input.mod_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "MODS_NO_MODID")); };
250 if input.list_id.is_none() { input.list = Some(get_current_list(config)?); };
251 Ok(input)
252 },
253 }
254}
255
256async fn check_list(mut input: Input, config: Cfg) -> MLE<Input> {
257 if input.list_options.is_none() {
258 return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_ARGUMENT"));
259 };
260 match input.clone().list_options.unwrap() {
261 ListOptions::Add => {
262 if input.list_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "no list id specified")); };
263 if input.list_mcversion.is_none() {
264 println!("No Minecraft Version specified, defaulting to latest release");
265 input.list_mcversion = Some(get_minecraft_version(&config.apis.modrinth, MCVersionType::Release).await);
266 };
267 if input.directory.is_none() {
268 let id = input.clone().list_id.unwrap();
269 println!("No download directory specified, defaulting to ./downloads/{}", id);
270 input.directory = Some(format!("./downloads/{}", id))
271 };
272 if input.modloader.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "no modloader specified")); };
273 Ok(input)
274 },
275 ListOptions::Remove => {
276 if input.list.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_SPECIFIED")); };
277 Ok(input)
278 },
279 ListOptions::Change => {
280 if input.list.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_SPECIFIED")); };
281 Ok(input)
282 },
283 ListOptions::Version => {
284 if input.list.is_none() {
285 println!("No list specified, using default");
286 input.list = Some(get_current_list(config)?);
287 };
288 Ok(input)
289 }
290 }
291}
292
293#[test]
294fn input_from() {
295 let config = Cfg::init("modlist.toml").unwrap();
296 assert_eq!(
297 Input::from(config, vec![String::from("-la test -lv 1.19.3")]).unwrap(),
298 Input {
299 command: Some(Cmd::List),
300 mod_options: None,
301 mod_id: None,
302 set_version: false,
303 all_lists: false,
304 clean: false,
305 direct_download: false,
306 delete_old: false,
307 list: None,
308 list_options: Some(ListOptions::Add),
309 list_id: Some(String::from("test")),
310 list_mcversion: Some(String::from("1.19.3")),
311 modloader: None,
312 directory: None,
313 io_options: None,
314 file: None,
315 }
316 );
317
318}
319
320#[tokio::test]
321async fn get_input_test() {
322 let config = Cfg::init("modlist.toml").unwrap();
323 assert_eq!(
324 get_input(config.clone(), vec![String::from("-ma test")]).await.unwrap(),
325 Input {
326 command: Some(Cmd::Mod),
327 mod_options: Some(ModOptions::Add),
328 mod_id: Some(IDSelector::ModificationID(String::from("test"))),
329 set_version: false,
330 all_lists: false,
331 clean: false,
332 direct_download: false,
333 delete_old: false,
334 list: Some(lists_get(config.clone(), String::from("one")).unwrap()),
335 list_options: None,
336 list_id: None,
337 list_mcversion: None,
338 modloader: None,
339 directory: None,
340 io_options: None,
341 file: None
342 }
343 )
344}
diff --git a/src/lib.rs b/src/lib.rs
index eb845d8..43f0fe7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,36 +1,37 @@
1pub mod apis; 1pub mod apis;
2pub mod config;
3pub mod commands; 2pub mod commands;
4pub mod input; 3pub mod config;
5pub mod db; 4pub mod db;
6pub mod error; 5pub mod error;
7pub mod files; 6pub mod files;
8 7
9use std::path::Path; 8use std::{fmt::Display, path::Path};
10 9
11pub use apis::*; 10pub use apis::*;
12pub use commands::*; 11pub use commands::*;
13use error::{MLE, ErrorType, MLError}; 12use error::{ErrorType, MLError, MLE};
14 13
15#[derive(Debug, Clone, PartialEq, Eq)] 14#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum Modloader { 15pub enum Modloader {
17 Fabric, 16 Fabric,
18 Forge 17 Forge,
19} 18}
20 19
21impl Modloader { 20impl Modloader {
22 fn to_string(&self) -> String { 21 pub fn from(string: &str) -> MLE<Modloader> {
23 match self {
24 Modloader::Fabric => String::from("fabric"),
25 Modloader::Forge => String::from("forge"),
26 }
27 }
28
29 fn from(string: &str) -> MLE<Modloader> {
30 match string { 22 match string {
31 "forge" => Ok(Modloader::Forge), 23 "forge" => Ok(Modloader::Forge),
32 "fabric" => Ok(Modloader::Fabric), 24 "fabric" => Ok(Modloader::Fabric),
33 _ => Err(MLError::new(ErrorType::ArgumentError, "UNKNOWN_MODLOADER")) 25 _ => Err(MLError::new(ErrorType::ArgumentError, "UNKNOWN_MODLOADER")),
26 }
27 }
28}
29
30impl Display for Modloader {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 Modloader::Fabric => write!(f, "fabric"),
34 Modloader::Forge => write!(f, "forge"),
34 } 35 }
35 } 36 }
36} 37}
diff --git a/src/main.rs b/src/main.rs
index 32727c7..2006856 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,65 +1,269 @@
1use std::{env, process}; 1use clap::{Parser, Subcommand};
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};
2 8
3use modlist::{config::Cfg, input::{get_input, Cmd}, update, download, list, io, modification, setup}; 9//TODO implement remote sql db
10
11//TODO make default list optional
12#[derive(Parser)]
13#[command(author, version, about)]
14struct Cli {
15 #[command(subcommand)]
16 command: Commands,
17}
18
19#[derive(Subcommand)]
20enum Commands {
21 r#Mod {
22 #[command(subcommand)]
23 command: ModCommands,
24 },
25 List {
26 #[command(subcommand)]
27 command: ListCommands,
28 },
29 Download {
30 /// download all lists
31 #[arg(short, long)]
32 all: bool,
33
34 /// clean all mods before downloading them
35 #[arg(short, long)]
36 clean: bool,
37
38 /// remove disabled versions
39 #[arg(short, long)]
40 remove: bool,
41 },
42 Update {
43 /// download all lists
44 #[arg(short, long)]
45 all: bool,
46
47 /// directly download updated mods
48 #[arg(short, long)]
49 download: bool,
50
51 /// clean all mods before downloading them
52 #[arg(short, long)]
53 clean: bool,
54
55 /// delete disabled versions
56 #[arg(short, long)]
57 remove: bool,
58 },
59 Import {
60 #[arg(short, long)]
61 file: Option<String>,
62
63 /// directly download imported mods
64 #[arg(short, long)]
65 download: bool,
66 },
67 Export {
68 /// the list you want to export
69 list: Option<String>,
70 },
71}
72
73#[derive(Subcommand)]
74enum ModCommands {
75 Add {
76 /// id of the mod/version
77 id: String,
78
79 /// set id mode to version
80 #[arg(short, long)]
81 version: bool,
82
83 /// directly download the mod
84 #[arg(short, long)]
85 download: bool,
86
87 /// lock the version added
88 #[arg(/* short , */long)]
89 lock: bool,
90
91 /// optional List selection, else default list will be used
92 #[arg(short, long)]
93 list: Option<String>,
94 },
95 Remove {
96 /// id, name or title of the mod
97 id: String,
98
99 /// optional List selection, else default list will be used
100 #[arg(short, long)]
101 list: Option<String>,
102 },
103}
104
105#[derive(Subcommand)]
106enum ListCommands {
107 Add {
108 /// list id
109 id: String,
110
111 directory: String,
112
113 modloader: Option<String>,
114
115 version: Option<String>,
116 },
117 Remove {
118 /// id, name or title of the list
119 id: String,
120 },
121 List,
122 Change {
123 /// id of the list to change to
124 id: String,
125 },
126 Version {
127 /// list id
128 id: String,
129 /// desired minecraft version
130 version: String,
131
132 /// directly download updated mods
133 #[arg(long, short)]
134 download: bool,
135
136 /// delete disabled versions
137 #[arg(short, long)]
138 remove: bool,
139 },
140}
4 141
5#[tokio::main] 142#[tokio::main]
6async fn main() { 143async fn main() {
144 let cli = Cli::parse();
145
7 let config = Cfg::init("modlist.toml").unwrap(); 146 let config = Cfg::init("modlist.toml").unwrap();
8 147 println!("{:?}", config);
9 let mut args: Vec<String> = env::args().collect(); 148
10 args.reverse(); 149 //TODO setup? maybe setup on install
11 args.pop(); 150 match cli.command {
12 args.reverse(); 151 Commands::Mod { command } => {
13 152 match command {
14 if args.is_empty() { 153 #[allow(unused_variables)]
15 println!("Please enter an argument"); 154 ModCommands::Add {
16 process::exit(1); 155 id,
17 }; 156 version,
18 157 list,
19 let input = match get_input(config.clone(), args).await { 158 download,
20 Ok(i) => i, 159 lock,
21 Err(e) => { 160 } => {
22 println!("{}", e); 161 let listf = match list {
23 process::exit(1); 162 Some(list) => lists_get(config.clone(), list).unwrap(),
163 None => lists_get(
164 config.clone(),
165 config_get_current_list(config.clone()).unwrap(),
166 )
167 .unwrap(),
168 };
169
170 let marked_id = match version {
171 true => IDSelector::VersionID(id),
172 false => IDSelector::ModificationID(id),
173 };
174
175 mod_add(config, vec![marked_id], listf, download, lock).await
176 }
177 ModCommands::Remove { id, list } => {
178 //TODO add output
179 //TODO add success even if no file found
180 let listf = match list {
181 Some(list) => lists_get(config.clone(), list).unwrap(),
182 None => lists_get(
183 config.clone(),
184 config_get_current_list(config.clone()).unwrap(),
185 )
186 .unwrap(),
187 };
188 mod_remove(config, &id, listf)
189 }
190 }
24 } 191 }
25 }; 192 Commands::List { command } => {
26 193 match command {
27 match input.clone().command.unwrap() { 194 ListCommands::Add {
28 Cmd::Mod => { 195 id,
29 modification(config, input).await 196 directory,
30 }, 197 modloader,
31 Cmd::List => { 198 version,
32 list(config, input).await 199 } => {
33 }, 200 let ml = match modloader {
34 Cmd::Update => { 201 Some(ml) => Modloader::from(&ml).unwrap(),
35 update(config, input).await 202 //TODO add default modloader to config
36 }, 203 None => Modloader::Fabric,
37 Cmd::Download => { 204 };
38 download(config, input).await
39 },
40 Cmd::Io => {
41 io(config, input).await
42 },
43 Cmd::Version => {
44 show_version();
45 Ok(())
46 },
47 Cmd::Setup => {
48 setup(config).await
49 },
50 }.unwrap()
51}
52 205
53fn show_version() { 206 let ver = match version {
54 match std::env::var("DEV") { 207 Some(ver) => ver,
55 Ok(dev) => { 208 //TODO get latest version
56 let devint = dev.parse::<i32>().unwrap(); 209 //TODO impl config for specific version or latest or latest snap
57 if devint >= 1 { 210 None => "1.19.4".to_string(),
58 println!("Modlist by FxQnLr v{} (DEV)", env!("CARGO_PKG_VERSION")); 211 };
212
213 list_add(config, id, ver, ml, directory)
214 }
215 ListCommands::Remove { id } => list_remove(config, id),
216 ListCommands::List => {
217 todo!()
218 }
219 ListCommands::Change { id } => list_change(config, id),
220 ListCommands::Version {
221 id,
222 version,
223 download,
224 remove,
225 } => list_version(config, id, version, download, remove).await,
226 }
227 }
228 //TODO a add specific list
229 Commands::Update {
230 all,
231 download,
232 clean,
233 remove,
234 } => {
235 let mut liststack: Vec<List> = vec![];
236 if all {
237 let list_ids = lists_get_all_ids(config.clone()).unwrap();
238 for id in list_ids {
239 liststack.push(lists_get(config.clone(), id).unwrap());
240 }
59 } else { 241 } else {
60 println!("Modlist by FxQnLr v{}", env!("CARGO_PKG_VERSION")); 242 let current = get_current_list(config.clone()).unwrap();
243 println!("Update list {}:", current.id);
244 liststack.push(current)
61 } 245 }
62 }, 246 update(config, liststack, clean, download, remove).await
63 Err(..) => println!("Modlist by FxQnLr v{}", env!("CARGO_PKG_VERSION")), 247 }
248 //TODO add specific list
249 Commands::Download { all, clean, remove } => download(config, all, clean, remove).await,
250 Commands::Import { file, download } => {
251 let filestr: String = match file {
252 Some(args) => args,
253 None => devdir(
254 dirs::home_dir()
255 .unwrap()
256 .join("mlexport.toml")
257 .into_os_string()
258 .into_string()
259 .unwrap()
260 .as_str(),
261 ),
262 };
263
264 import(config, filestr, download).await
265 }
266 Commands::Export { list } => export(config, list),
64 } 267 }
268 .unwrap();
65} 269}