summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfxqnlr <[email protected]>2023-01-22 22:34:17 +0100
committerfxqnlr <[email protected]>2023-01-22 22:34:17 +0100
commit1890d59428dfcca861ea1b7820411d80cc60d713 (patch)
tree559a41a814a33a72ccab3640a6c81d10451f1683
parentf7a6d2e9c67c1fdf8fc17fa0461a201fd2720537 (diff)
downloadmodlist-1890d59428dfcca861ea1b7820411d80cc60d713.tar
modlist-1890d59428dfcca861ea1b7820411d80cc60d713.tar.gz
modlist-1890d59428dfcca861ea1b7820411d80cc60d713.zip
Added list version cmd, fixed some todos
-rw-r--r--.gitignore1
-rw-r--r--planmodlist.xoppbin322225 -> 322154 bytes
-rw-r--r--src/apis/modrinth.rs10
-rw-r--r--src/commands/download.rs13
-rw-r--r--src/commands/list.rs30
-rw-r--r--src/commands/mod.rs4
-rw-r--r--src/commands/modification.rs45
-rw-r--r--src/commands/setup.rs2
-rw-r--r--src/commands/update.rs67
-rw-r--r--src/config.rs1
-rw-r--r--src/db.rs26
-rw-r--r--src/error.rs2
-rw-r--r--src/files.rs29
-rw-r--r--src/input.rs46
-rw-r--r--src/main.rs2
15 files changed, 151 insertions, 127 deletions
diff --git a/.gitignore b/.gitignore
index 693fb15..8713145 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ data.db.cp
8export.toml 8export.toml
9config.toml 9config.toml
10/dev 10/dev
11/.fleet \ No newline at end of file
diff --git a/planmodlist.xopp b/planmodlist.xopp
index e8920e0..aeb2fa7 100644
--- a/planmodlist.xopp
+++ b/planmodlist.xopp
Binary files differ
diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs
index 9890cf2..36ab5df 100644
--- a/src/apis/modrinth.rs
+++ b/src/apis/modrinth.rs
@@ -1,4 +1,3 @@
1use std::io::{Error, ErrorKind};
2use chrono::{DateTime, FixedOffset}; 1use chrono::{DateTime, FixedOffset};
3use reqwest::Client; 2use reqwest::Client;
4use serde::Deserialize; 3use serde::Deserialize;
@@ -142,7 +141,7 @@ pub async fn project(api: String, name: &str) -> Project {
142} 141}
143 142
144pub async fn projects(api: String, ids: Vec<String>) -> Vec<Project> { 143pub async fn projects(api: String, ids: Vec<String>) -> Vec<Project> {
145 println!("Getting versions for all mods from modrinth"); 144 println!("\tGet versions from modrinth\n");
146 let all = ids.join(r#"",""#); 145 let all = ids.join(r#"",""#);
147 let url = format!(r#"projects?ids=["{}"]"#, all); 146 let url = format!(r#"projects?ids=["{}"]"#, all);
148 147
@@ -188,7 +187,7 @@ pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
188 } 187 }
189 times.sort_by_key(|t| t.1); 188 times.sort_by_key(|t| t.1);
190 times.reverse(); 189 times.reverse();
191 println!("Current Version: {}", times[0].0); 190 println!("\t └New current version: {}", times[0].0);
192 Ok(times[0].0.to_string()) 191 Ok(times[0].0.to_string())
193 }, 192 },
194 _ => panic!("available_versions should never be negative"), 193 _ => panic!("available_versions should never be negative"),
@@ -198,6 +197,7 @@ pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
198pub enum MCVersionType { 197pub enum MCVersionType {
199 Release, 198 Release,
200 Latest, 199 Latest,
200 Specific,
201} 201}
202 202
203#[derive(Debug, Deserialize)] 203#[derive(Debug, Deserialize)]
@@ -220,6 +220,10 @@ pub async fn get_minecraft_version(api: String, version: MCVersionType) -> Strin
220 &mc_versions[i] 220 &mc_versions[i]
221 }, 221 },
222 MCVersionType::Latest => &mc_versions[0], 222 MCVersionType::Latest => &mc_versions[0],
223 MCVersionType::Specific => {
224 println!("Not inplemented");
225 &mc_versions[0]
226 }
223 }; 227 };
224 String::from(&ver.version) 228 String::from(&ver.version)
225} 229}
diff --git a/src/commands/download.rs b/src/commands/download.rs
index 0f63876..7748d15 100644
--- a/src/commands/download.rs
+++ b/src/commands/download.rs
@@ -1,4 +1,4 @@
1use crate::{files::{get_downloaded_versions, download_versions, delete_version, disable_version}, db::{userlist_get_all_current_versions_with_mods, lists_get_all_ids, lists_get}, modrinth::get_raw_versions, error::{MLE, ErrorType, MLError}}; 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}};
2use crate::{List, get_current_list, config::Cfg, input::Input}; 2use crate::{List, get_current_list, config::Cfg, input::Input};
3 3
4pub async fn download(config: Cfg, input: Input) -> MLE<()> { 4pub async fn download(config: Cfg, input: Input) -> MLE<()> {
@@ -44,17 +44,10 @@ pub async fn download(config: Cfg, input: Input) -> MLE<()> {
44 } 44 }
45 } 45 }
46 46
47 if input.clean { 47 if input.clean { clean_list_dir(&current_list)? };
48 let dl_path = &current_list.download_folder;
49 println!("Cleaning {}", dl_path);
50 for entry in std::fs::read_dir(dl_path)? {
51 let entry = entry?;
52 std::fs::remove_file(entry.path())?;
53 }
54 }
55 48
56 if !to_download.is_empty() { 49 if !to_download.is_empty() {
57 download_versions(current_list.clone(), get_raw_versions(String::from(&config.apis.modrinth), to_download).await).await?; 50 download_versions(current_list.clone(), config.clone(), get_raw_versions(String::from(&config.apis.modrinth), to_download).await).await?;
58 } else { 51 } else {
59 println!("There are no new versions to download"); 52 println!("There are no new versions to download");
60 } 53 }
diff --git a/src/commands/list.rs b/src/commands/list.rs
index bc58787..eaf6fa1 100644
--- a/src/commands/list.rs
+++ b/src/commands/list.rs
@@ -1,4 +1,4 @@
1use crate::{db::{lists_insert, lists_remove, config_change_current_list, config_get_current_list, lists_get}, Modloader, config::Cfg, input::{Input, ListOptions}, /*cmd_update,*/ error::MLE, /*modrinth::MCVersionType*/}; 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};
2 2
3#[derive(Debug, Clone, PartialEq, Eq)] 3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct List { 4pub struct List {
@@ -20,13 +20,9 @@ pub async fn list(config: Cfg, input: Input) -> MLE<()> {
20 ListOptions::Remove => { 20 ListOptions::Remove => {
21 remove(config, input) 21 remove(config, input)
22 }, 22 },
23 /* 23 ListOptions::Version => {
24 Subcmd::Version => { 24 version(config, input).await
25 match version(config, Some(input.args.ok_or("NO_VERSION")?), Some(MCVersionType::Release)).await { 25 }
26 Ok(..) => Ok(()),
27 Err(e) => Err(Box::new(e))
28 }
29 }*/
30 } 26 }
31} 27}
32 28
@@ -44,7 +40,7 @@ fn add(config: Cfg, input: Input) -> MLE<()> {
44} 40}
45 41
46fn change(config: Cfg, input: Input) -> MLE<()> { 42fn change(config: Cfg, input: Input) -> MLE<()> {
47 //TODO reimplement current list 43 println!("Change default list to: {}", input.clone().list.unwrap().id);
48 config_change_current_list(config, input.list.unwrap().id) 44 config_change_current_list(config, input.list.unwrap().id)
49} 45}
50 46
@@ -52,17 +48,19 @@ fn remove(config: Cfg, input: Input) -> MLE<()> {
52 lists_remove(config, input.list.unwrap().id) 48 lists_remove(config, input.list.unwrap().id)
53} 49}
54 50
55/*
56///Changing the current lists version and updating it 51///Changing the current lists version and updating it
57/// #Arguments 52/// #Arguments
58/// 53///
59/// * `config` - The current config 54/// * `config` - The current config
60/// * `args` - All args, to extract the new version 55/// * `args` - All args, to extract the new version
61async fn version(config: Cfg, args: Option<Vec<String>>, version_type: Option<MCVersionType>) -> MLE<()> { 56async fn version(config: Cfg, input: Input) -> MLE<()> {
62 let current_list = lists_get(config.clone(), config_get_current_list(config.clone())?)?; 57 println!("Change version for list {} to minecraft version: {}", input.clone().list.unwrap().id, input.clone().list_mcversion.unwrap());
63 58
64 lists_version(config.clone(), String::from(&current_list.id), String::from(&args.unwrap()[0]))?; 59 lists_version(config.clone(), input.clone().list.ok_or("").unwrap().id, input.clone().list_mcversion.ok_or("").unwrap())?;
65 //update the list & with -- args 60
66 cmd_update(config, vec![current_list], true, true, false).await 61 //Linebreak readability
62 println!("");
63
64 println!("Check for updates for new minecraft version in list {}", input.clone().list.unwrap().id);
65 cmd_update(config, vec![input.list.ok_or("").unwrap()], true, input.direct_download, input.delete_old).await
67} 66}
68*/
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 527afc7..38139f9 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -1,11 +1,11 @@
1//pub mod modification; 1pub mod modification;
2pub mod list; 2pub mod list;
3pub mod update; 3pub mod update;
4//pub mod setup; 4//pub mod setup;
5pub mod download; 5pub mod download;
6pub mod io; 6pub mod io;
7 7
8//pub use modification::*; 8pub use modification::*;
9pub use list::*; 9pub use list::*;
10pub use update::*; 10pub use update::*;
11//pub use setup::*; 11//pub use setup::*;
diff --git a/src/commands/modification.rs b/src/commands/modification.rs
index 7d4be8d..6a03b35 100644
--- a/src/commands/modification.rs
+++ b/src/commands/modification.rs
@@ -1,33 +1,24 @@
1use std::io::{Error, ErrorKind}; 1use crate::{modrinth::{project, versions, extract_current_version, Version, projects}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, mods_get_all_ids, 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}};
2 2
3use crate::{modrinth::{project, versions, extract_current_version, Version, projects}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, mods_get_all_ids, userlist_get_all_ids, userlist_get_current_version, lists_get_all_ids, mods_remove}, input::{Input, Subcmd}, get_current_list, files::{delete_version, download_versions}, List}; 3pub async fn modification(config: Cfg, input: Input) -> MLE<()> {
4 4 match input.clone().mod_options.ok_or("").unwrap() {
5pub async fn modification(config: Cfg, input: Input) -> Result<(), Box<dyn std::error::Error>> { 5 ModOptions::Add => {
6 match input.subcommand.as_ref().ok_or("")? {
7 Subcmd::Add => {
8 add(config, input).await 6 add(config, input).await
9 }, 7 },
10 Subcmd::Remove => { 8 ModOptions::Remove => {
11 remove(config, input.args.ok_or("")?) 9 remove(config, input)
12 }, 10 },
13 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "SUBCOMMAND_NOT_AVAILABLE")))
14 } 11 }
15} 12}
16 13
17async fn add(config: Cfg, input: Input) -> Result<(), Box<dyn std::error::Error>> { 14async fn add(config: Cfg, input: Input) -> MLE<()> {
18
19 let args = input.args.ok_or("")?;
20 15
21 if args.is_empty() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))); }; 16 mod_add(config, vec![String::from(input.mod_id.unwrap())], input.list.unwrap(), input.direct_download).await?;
22
23 let current_list = get_current_list(config.clone())?;
24
25 mod_add(config, vec![String::from(&args[0])], current_list, input.disable_download).await?;
26 17
27 Ok(()) 18 Ok(())
28} 19}
29 20
30pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, disable_download: bool) -> Result<(), Box<dyn std::error::Error>> { 21pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, disable_download: bool) -> MLE<()> {
31 22
32 println!("Adding mod(s) {:?}", mod_id); 23 println!("Adding mod(s) {:?}", mod_id);
33 let projects = if mod_id.len() == 1 { 24 let projects = if mod_id.len() == 1 {
@@ -50,7 +41,7 @@ pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, disable_downl
50 41
51 current_version_id = current_version.clone().unwrap().id; 42 current_version_id = current_version.clone().unwrap().id;
52 43
53 file = current_version.clone().ok_or("VERSION_CORRUPTED")?.files.into_iter().find(|f| f.primary).unwrap().url; 44 file = current_version.clone().ok_or("").unwrap().files.into_iter().find(|f| f.primary).unwrap().url;
54 45
55 for ver in available_versions { 46 for ver in available_versions {
56 available_versions_vec.push(ver.id); 47 available_versions_vec.push(ver.id);
@@ -67,7 +58,7 @@ pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, disable_downl
67 match userlist_get_all_ids(config.clone(), list.clone().id) { 58 match userlist_get_all_ids(config.clone(), list.clone().id) {
68 Ok(mods) => { 59 Ok(mods) => {
69 if mods.contains(&project.id) { 60 if mods.contains(&project.id) {
70 return Err(Box::new(Error::new(ErrorKind::Other, "MOD_ALREADY_ON_LIST"))); } 61 return Err(MLError::new(ErrorType::ModError, "MOD_ALREADY_ON_LIST")); }
71 else { 62 else {
72 userlist_insert(config.clone(), String::from(&list.id), String::from(&project.id), String::from(&current_version_id), available_versions_vec, file)?; 63 userlist_insert(config.clone(), String::from(&list.id), String::from(&project.id), String::from(&current_version_id), available_versions_vec, file)?;
73 } 64 }
@@ -88,24 +79,22 @@ pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, disable_downl
88 }, 79 },
89 }; 80 };
90 81
91 if !disable_download && current_version.is_some() { download_versions(list.clone(), vec![current_version.unwrap()]).await?; }; 82 if !disable_download && current_version.is_some() { download_versions(list.clone(), config.clone(), vec![current_version.unwrap()]).await?; };
92 83
93 } 84 }
94 85
95 Ok(()) 86 Ok(())
96} 87}
97 88
98fn remove(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> { 89fn remove(config: Cfg, input: Input) -> MLE<()> {
99 if args.is_empty() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))); };
100 90
101 let current_list = get_current_list(config.clone())?; 91 let mod_id = mods_get_id(config.clone(), input.clone().mod_id.unwrap())?;
102 let mod_id = mods_get_id(config.clone(), String::from(&args[0]))?;
103 92
104 let version = userlist_get_current_version(config.clone(), String::from(&current_list.id), String::from(&mod_id))?; 93 let version = userlist_get_current_version(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?;
105 94
106 //TODO implement remove from modlist if not in any other lists && config clean is true 95 //TODO implement remove from modlist if not in any other lists && config clean is true
107 userlist_remove(config.clone(), String::from(&current_list.id), String::from(&mod_id))?; 96 userlist_remove(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?;
108 delete_version(current_list, version)?; 97 delete_version(input.list.unwrap(), version)?;
109 98
110 let list_ids = lists_get_all_ids(config.clone())?; 99 let list_ids = lists_get_all_ids(config.clone())?;
111 100
diff --git a/src/commands/setup.rs b/src/commands/setup.rs
index e4fa801..cc7472c 100644
--- a/src/commands/setup.rs
+++ b/src/commands/setup.rs
@@ -63,4 +63,4 @@ fn to_04(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
63 s_insert_column(config.clone(), list_id, String::from("disabled_versions"), String::from("TEXT"), Some(String::from("NONE")))?; 63 s_insert_column(config.clone(), list_id, String::from("disabled_versions"), String::from("TEXT"), Some(String::from("NONE")))?;
64 } 64 }
65 s_config_update_version(config, String::from("0.4")) 65 s_config_update_version(config, String::from("0.4"))
66} 66} \ No newline at end of file
diff --git a/src/commands/update.rs b/src/commands/update.rs
index 068c3f3..f8bdb82 100644
--- a/src/commands/update.rs
+++ b/src/commands/update.rs
@@ -1,7 +1,6 @@
1use crate::{config::Cfg, modrinth::{projects, Project, versions, extract_current_version, Version}, get_current_list, db::{userlist_get_all_ids, mods_get_versions, userlist_get_applicable_versions, userlist_change_versions, lists_get_all_ids, lists_get, userlist_get_current_version, mods_change_versions}, List, input::Input, files::{delete_version, download_versions, disable_version}, error::{MLE, MLError, ErrorType}}; 1use crate::{config::Cfg, modrinth::{projects, Project, versions, extract_current_version, Version}, get_current_list, db::{userlist_get_all_ids, mods_get_versions, userlist_get_applicable_versions, userlist_change_versions, lists_get_all_ids, lists_get, userlist_get_current_version, mods_change_versions}, List, input::Input, files::{delete_version, download_versions, disable_version, clean_list_dir}, error::{MLE, MLError, ErrorType}};
2 2
3pub async fn update(config: Cfg, input: Input) -> MLE<()> { 3pub async fn update(config: Cfg, input: Input) -> MLE<()> {
4
5 let mut liststack: Vec<List> = vec![]; 4 let mut liststack: Vec<List> = vec![];
6 if input.all_lists { 5 if input.all_lists {
7 let list_ids = lists_get_all_ids(config.clone())?; 6 let list_ids = lists_get_all_ids(config.clone())?;
@@ -10,10 +9,9 @@ pub async fn update(config: Cfg, input: Input) -> MLE<()> {
10 } 9 }
11 } else { 10 } else {
12 let current = get_current_list(config.clone())?; 11 let current = get_current_list(config.clone())?;
13 println!("Checking for updates of mods in {}", current.id); 12 println!("Check for updates of mods in list {}", current.id);
14 liststack.push(current) 13 liststack.push(current)
15 } 14 }
16
17 cmd_update(config, liststack, input.clean, input.direct_download, input.delete_old).await 15 cmd_update(config, liststack, input.clean, input.direct_download, input.delete_old).await
18} 16}
19 17
@@ -29,6 +27,7 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
29 let mut projects = projects(String::from(&config.apis.modrinth), mods).await; 27 let mut projects = projects(String::from(&config.apis.modrinth), mods).await;
30 projects.sort_by_key(|pro| pro.id.clone()); 28 projects.sort_by_key(|pro| pro.id.clone());
31 29
30 println!("Comparing mod versions:");
32 let mut updatestack: Vec<Version> = vec![]; 31 let mut updatestack: Vec<Version> = vec![];
33 for (index, project) in projects.into_iter().enumerate() { 32 for (index, project) in projects.into_iter().enumerate() {
34 //Get versions for project and check if they match up 33 //Get versions for project and check if they match up
@@ -37,6 +36,8 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
37 let v_id = &current_version.mod_id; 36 let v_id = &current_version.mod_id;
38 if &p_id != v_id { return Err(MLError::new(ErrorType::Other, "SORTING_ERROR")) }; 37 if &p_id != v_id { return Err(MLError::new(ErrorType::Other, "SORTING_ERROR")) };
39 38
39 println!("\t({}) Check for update", project.title);
40
40 //Getting current installed version for disable or delete 41 //Getting current installed version for disable or delete
41 let disable_version = userlist_get_current_version(config.clone(), String::from(&current_list.id), String::from(&project.id))?; 42 let disable_version = userlist_get_current_version(config.clone(), String::from(&current_list.id), String::from(&project.id))?;
42 43
@@ -49,40 +50,44 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
49 current_versions.push((disable_version, p_id)); 50 current_versions.push((disable_version, p_id));
50 ver 51 ver
51 }, 52 },
52 //TODO handle errors (only continue on "NO_UPDATE_AVAILABLE") 53 Err(e) => {
53 Err(..) => { 54 //Catch no update available
54 //Updating versions in modlist for no repeating version calls 55 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" {
55 mods_change_versions(config.clone(), version_db_string, project.id)?; 56 mods_change_versions(config.clone(), version_db_string, project.id)?;
56 println!("({}) No new version found for the specified", project.title); 57 println!("\t └No new version found for the specified minecraft version");
58 } else {
59 return Err(e);
60 };
57 continue; 61 continue;
58 }, 62 },
59 }); 63 });
60 } else { 64 } else {
61 println!("({}) No new version found", project.title); 65 println!("\t No new version found");
62 }; 66 };
63 }; 67 };
68
69 //Linebreak readability
70 println!("");
64 71
65 if clean { 72 if clean { clean_list_dir(&current_list)? };
66 let dl_path = &current_list.download_folder;
67 println!("Cleaning {}", dl_path);
68 for entry in std::fs::read_dir(dl_path)? {
69 let entry = entry?;
70 std::fs::remove_file(entry.path())?;
71 }
72 }
73 73
74 //Linebreak readability
75 println!("");
76
74 if direct_download { 77 if direct_download {
75 download_versions(current_list.clone(), updatestack).await?; 78 download_versions(current_list.clone(), config.clone(), updatestack).await?;
76 79
77 //Disable old versions 80 //Disable old versions
78 for ver in current_versions { 81 if !clean {
79 if delete_old { 82 for ver in current_versions {
80 println!("Deleting version {} for mod {}", ver.0, ver.1); 83 if delete_old {
81 delete_version(current_list.clone(), ver.0)?; 84 println!("Deleting version {} for mod {}", ver.0, ver.1);
82 } else if ver.0 != "NONE" { 85 delete_version(current_list.clone(), ver.0)?;
83 println!("Disabling version {} for mod {}", ver.0, ver.1); 86 } else if ver.0 != "NONE" {
84 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?; 87 println!("Disabling version {} for mod {}", ver.0, ver.1);
85 }; 88 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?;
89 };
90 }
86 } 91 }
87 }; 92 };
88 93
@@ -92,8 +97,6 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
92} 97}
93 98
94async fn specific_update(config: Cfg, clean: bool, list: List, project: Project) -> MLE<Version> { 99async fn specific_update(config: Cfg, clean: bool, list: List, project: Project) -> MLE<Version> {
95 println!("Checking update for '{}' in {}", project.title, list.id);
96
97 let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await; 100 let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await;
98 101
99 let mut versions: Vec<String> = vec![]; 102 let mut versions: Vec<String> = vec![];
@@ -110,7 +113,7 @@ async fn specific_update(config: Cfg, clean: bool, list: List, project: Project)
110 let mut current: Vec<Version> = vec![]; 113 let mut current: Vec<Version> = vec![];
111 if clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&project.id))?) { 114 if clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&project.id))?) {
112 //get new versions 115 //get new versions
113 print!(" | getting new version"); 116 println!("\t └Get versions for specified minecraft versions");
114 let current_str = extract_current_version(applicable_versions.clone())?; 117 let current_str = extract_current_version(applicable_versions.clone())?;
115 let current_ver = match applicable_versions.into_iter().find(|ver| ver.id == current_str).ok_or("!no current version in applicable_versions") { 118 let current_ver = match applicable_versions.into_iter().find(|ver| ver.id == current_str).ok_or("!no current version in applicable_versions") {
116 Ok(v) => Ok(v), 119 Ok(v) => Ok(v),
@@ -122,12 +125,12 @@ async fn specific_update(config: Cfg, clean: bool, list: List, project: Project)
122 Ok(p) => Ok(p), 125 Ok(p) => Ok(p),
123 Err(e) => Err(MLError::new(ErrorType::Other, e)), 126 Err(e) => Err(MLError::new(ErrorType::Other, e)),
124 }?.url; 127 }?.url;
125 userlist_change_versions(config, list.id, current_str, versions.join("|"), link, project.id); 128 userlist_change_versions(config, list.id, current_str, versions.join("|"), link, project.id)?;
126 } 129 }
127 130
128 if current.is_empty() { return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE")) }; 131 if current.is_empty() { return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE")) };
129 132
130 println!(" | ✔️"); 133 //println!(" ������️");
131 Ok(current[0].clone()) 134 Ok(current[0].clone())
132} 135}
133 136
diff --git a/src/config.rs b/src/config.rs
index 383e7ee..ded0062 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -25,7 +25,6 @@ impl Cfg {
25 if err.kind() == std::io::ErrorKind::NotFound { 25 if err.kind() == std::io::ErrorKind::NotFound {
26 println!("No config file found, creating one"); 26 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/") } }; 27 let default_cfg = Cfg { data: String::from("./"), apis: Apis { modrinth: String::from("https://api.modrinth.com/v2/") } };
28 //TODO Error
29 let mut file = File::create(devdir(configfile.to_str().unwrap()))?; 28 let mut file = File::create(devdir(configfile.to_str().unwrap()))?;
30 println!("Created config file"); 29 println!("Created config file");
31 file.write_all(&toml::to_string(&default_cfg)?.as_bytes())?; 30 file.write_all(&toml::to_string(&default_cfg)?.as_bytes())?;
diff --git a/src/db.rs b/src/db.rs
index 119b2a5..f47bda6 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -5,7 +5,7 @@ use rusqlite::Connection;
5use crate::{Modloader, config::Cfg, List, devdir, error::{MLE, MLError, ErrorType}}; 5use crate::{Modloader, config::Cfg, List, devdir, error::{MLE, MLError, ErrorType}};
6 6
7//mods 7//mods
8pub fn mods_insert(config: Cfg, id: String, name: String, versions: Vec<String>) -> Result<(), Box<dyn std::error::Error>> { 8pub fn mods_insert(config: Cfg, id: String, name: String, versions: Vec<String>) -> MLE<()> {
9 9
10 println!("Inserting mod {}({}) into database", name, id); 10 println!("Inserting mod {}({}) into database", name, id);
11 11
@@ -41,7 +41,7 @@ pub fn mods_get_all_ids(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::
41 } 41 }
42} 42}
43 43
44pub fn mods_get_id(config: Cfg, name: String) -> Result<String, Box<dyn std::error::Error>> { 44pub fn mods_get_id(config: Cfg, name: String) -> MLE<String> {
45 let data = devdir(format!("{}/data.db", config.data).as_str()); 45 let data = devdir(format!("{}/data.db", config.data).as_str());
46 let connection = Connection::open(data)?; 46 let connection = Connection::open(data)?;
47 47
@@ -56,12 +56,12 @@ pub fn mods_get_id(config: Cfg, name: String) -> Result<String, Box<dyn std::err
56 }; 56 };
57 57
58 match mod_id.is_empty() { 58 match mod_id.is_empty() {
59 true => Err(Box::new(Error::new(ErrorKind::NotFound, "MOD_NOT_FOUND"))), 59 true => Err(MLError::new(ErrorType::DBError, "GI_MOD_NOT_FOUND")),
60 false => Ok(mod_id), 60 false => Ok(mod_id),
61 } 61 }
62} 62}
63 63
64pub fn mods_get_name(config: Cfg, id: String) -> Result<String, Box<dyn std::error::Error>> { 64pub fn mods_get_name(config: Cfg, id: &str) -> MLE<String> {
65 let data = devdir(format!("{}/data.db", config.data).as_str()); 65 let data = devdir(format!("{}/data.db", config.data).as_str());
66 let connection = Connection::open(data)?; 66 let connection = Connection::open(data)?;
67 67
@@ -76,14 +76,14 @@ pub fn mods_get_name(config: Cfg, id: String) -> Result<String, Box<dyn std::err
76 }; 76 };
77 77
78 match mod_name.is_empty() { 78 match mod_name.is_empty() {
79 true => Err(Box::new(Error::new(ErrorKind::NotFound, "MOD_NOT_FOUND"))), 79 true => Err(MLError::new(ErrorType::DBError, "GN_MOD_NOT_FOUND")),
80 false => Ok(mod_name), 80 false => Ok(mod_name),
81 } 81 }
82} 82}
83 83
84pub fn mods_change_versions(config: Cfg, versions: String, mod_id: String) -> MLE<()> { 84pub fn mods_change_versions(config: Cfg, versions: String, mod_id: String) -> MLE<()> {
85 85
86 println!("Updating versions for {} with \n {}", mod_id, versions); 86 //println!("Updating versions for {} with \n {}", mod_id, versions);
87 87
88 let data = devdir(format!("{}/data.db", config.data).as_str()); 88 let data = devdir(format!("{}/data.db", config.data).as_str());
89 let connection = Connection::open(data)?; 89 let connection = Connection::open(data)?;
@@ -92,7 +92,7 @@ pub fn mods_change_versions(config: Cfg, versions: String, mod_id: String) -> ML
92 Ok(()) 92 Ok(())
93} 93}
94 94
95pub fn mods_remove(config: Cfg, id: String) -> Result<(), Box<dyn std::error::Error>> { 95pub fn mods_remove(config: Cfg, id: String) -> MLE<()> {
96 96
97 println!("Removing mod {} from database", id); 97 println!("Removing mod {} from database", id);
98 98
@@ -131,7 +131,7 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
131 131
132 for ver in id_iter { 132 for ver in id_iter {
133 let version = ver?; 133 let version = ver?;
134 println!("Getting versions for {} from the database", String::from(&version[2])); 134 println!("\t({}) Get versions from the database", String::from(&version[2]));
135 //println!("Found versions {} for mod {}", version[1], version[0]); 135 //println!("Found versions {} for mod {}", version[1], version[0]);
136 versionmaps.push(DBModlistVersions { mod_id: String::from(&version[0]), versions: String::from(&version[1]) }) 136 versionmaps.push(DBModlistVersions { mod_id: String::from(&version[0]), versions: String::from(&version[1]) })
137 }; 137 };
@@ -143,7 +143,7 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
143} 143}
144 144
145//userlist 145//userlist
146pub fn userlist_insert(config: Cfg, list_id: String, mod_id: String, current_version: String, applicable_versions: Vec<String>, current_link: String) -> Result<(), Box<dyn std::error::Error>> { 146pub fn userlist_insert(config: Cfg, list_id: String, mod_id: String, current_version: String, applicable_versions: Vec<String>, current_link: String) -> MLE<()> {
147 println!("Inserting {} into current list({})", mod_id, list_id); 147 println!("Inserting {} into current list({})", mod_id, list_id);
148 148
149 let data = devdir(format!("{}/data.db", config.data).as_str()); 149 let data = devdir(format!("{}/data.db", config.data).as_str());
@@ -201,7 +201,7 @@ pub fn userlist_get_applicable_versions(config: Cfg, list_id: String, mod_id: St
201 }; 201 };
202 202
203 match version.is_empty() { 203 match version.is_empty() {
204 true => Err(MLError::new(ErrorType::DBError, "MOD_NOT_FOUND")), 204 true => Err(MLError::new(ErrorType::DBError, "GAV_MOD_NOT_FOUND")),
205 false => Ok(version), 205 false => Ok(version),
206 } 206 }
207} 207}
@@ -241,7 +241,7 @@ pub fn userlist_get_current_version(config: Cfg, list_id: String, mod_id: String
241 }; 241 };
242 242
243 match version.is_empty() { 243 match version.is_empty() {
244 true => Err(MLError::new(ErrorType::DBError, "MOD_NOT_FOUND")), 244 true => Err(MLError::new(ErrorType::DBError, "GCV_MOD_NOT_FOUND")),
245 false => Ok(version), 245 false => Ok(version),
246 } 246 }
247} 247}
@@ -285,7 +285,7 @@ pub fn userlist_get_all_current_versions_with_mods(config: Cfg, list_id: String)
285 Ok(versions) 285 Ok(versions)
286} 286}
287 287
288pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: String, versions: String, link: String, mod_id: String) -> Result<(), Box<dyn std::error::Error>> { 288pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: String, versions: String, link: String, mod_id: String) -> MLE<()> {
289 let data = devdir(format!("{}/data.db", config.data).as_str()); 289 let data = devdir(format!("{}/data.db", config.data).as_str());
290 let connection = Connection::open(data)?; 290 let connection = Connection::open(data)?;
291 291
@@ -322,7 +322,7 @@ pub fn userlist_get_disabled_versions(config:Cfg, list_id: String, mod_id: Strin
322 }; 322 };
323 323
324 match version.is_empty() { 324 match version.is_empty() {
325 true => Err(MLError::new(ErrorType::DBError, "MOD_NOT_FOUND")), 325 true => Err(MLError::new(ErrorType::DBError, "GDV_MOD_NOT_FOUND")),
326 false => Ok(version), 326 false => Ok(version),
327 } 327 }
328} 328}
diff --git a/src/error.rs b/src/error.rs
index a1f5f2e..612a2e2 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -36,7 +36,7 @@ impl fmt::Display for MLError {
36 ErrorType::ArgumentError => write!(f, "User input not accepted: {}", self.message), 36 ErrorType::ArgumentError => write!(f, "User input not accepted: {}", self.message),
37 ErrorType::ArgumentCountError => write!(f, "Too many/too few arguments"), 37 ErrorType::ArgumentCountError => write!(f, "Too many/too few arguments"),
38 ErrorType::ConfigError => write!(f, "CONFIG"), 38 ErrorType::ConfigError => write!(f, "CONFIG"),
39 ErrorType::DBError => write!(f, "DATABASE"), 39 ErrorType::DBError => write!(f, "Database: {}", self.message),
40 ErrorType::ModError => write!(f, "Mod: {}", self.message), 40 ErrorType::ModError => write!(f, "Mod: {}", self.message),
41 ErrorType::LibToml => write!(f, "TOML"), 41 ErrorType::LibToml => write!(f, "TOML"),
42 ErrorType::LibSql => write!(f, "SQL"), 42 ErrorType::LibSql => write!(f, "SQL"),
diff --git a/src/files.rs b/src/files.rs
index 14e5636..0d7dc0a 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -2,13 +2,19 @@ use std::{fs::{File, read_dir, remove_file, rename}, io::Write, collections::Has
2use futures_util::StreamExt; 2use futures_util::StreamExt;
3use reqwest::Client; 3use reqwest::Client;
4 4
5use crate::{List, modrinth::Version, db::userlist_add_disabled_versions, config::Cfg, error::{MLE, MLError, ErrorType}}; 5use crate::{List, modrinth::Version, db::{userlist_add_disabled_versions, mods_get_name}, config::Cfg, error::{MLE, MLError, ErrorType}};
6 6
7pub async fn download_versions(current_list: List, versions: Vec<Version>) -> MLE<String> { 7pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> {
8 8
9 let dl_path = String::from(&current_list.download_folder); 9 let dl_path = String::from(&list.download_folder);
10
11 println!("Download to directory from: {} ({})", list.id, dl_path);
10 12
11 for ver in versions { 13 for ver in versions {
14 //TODO get project name instead of projectid from db
15 let project_name = mods_get_name(config.clone(), &ver.project_id)?;
16 print!("\t({})Download version {}", project_name, ver.id);
17 std::io::stdout().flush().unwrap();
12 let primary_file = ver.files.into_iter().find(|file| file.primary).unwrap(); 18 let primary_file = ver.files.into_iter().find(|file| file.primary).unwrap();
13 let mut splitname: Vec<&str> = primary_file.filename.split('.').collect(); 19 let mut splitname: Vec<&str> = primary_file.filename.split('.').collect();
14 let extension = match splitname.pop().ok_or("") { 20 let extension = match splitname.pop().ok_or("") {
@@ -16,14 +22,15 @@ pub async fn download_versions(current_list: List, versions: Vec<Version>) -> ML
16 Err(..) => return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")), 22 Err(..) => return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")),
17 }; 23 };
18 let filename = format!("{}.mr.{}.{}.{}", splitname.join("."), ver.project_id, ver.id, extension); 24 let filename = format!("{}.mr.{}.{}.{}", splitname.join("."), ver.project_id, ver.id, extension);
19 download_file(primary_file.url, current_list.clone().download_folder, filename).await?; 25 download_file(primary_file.url, list.clone().download_folder, filename).await?;
26 tokio::time::sleep(std::time::Duration::new(3, 0)).await;
27 println!(" ✓");
20 } 28 }
21 29
22 Ok(dl_path) 30 Ok(dl_path)
23} 31}
24 32
25async fn download_file(url: String, path: String, name: String) -> MLE<()> { 33async fn download_file(url: String, path: String, name: String) -> MLE<()> {
26 println!("Downloading {}", url);
27 let dl_path_file = format!("{}/{}", path, name); 34 let dl_path_file = format!("{}/{}", path, name);
28 let res = Client::new() 35 let res = Client::new()
29 .get(String::from(&url)) 36 .get(String::from(&url))
@@ -43,7 +50,7 @@ async fn download_file(url: String, path: String, name: String) -> MLE<()> {
43} 50}
44 51
45pub fn disable_version(config: Cfg, current_list: List, versionid: String, mod_id: String) -> MLE<()> { 52pub fn disable_version(config: Cfg, current_list: List, versionid: String, mod_id: String) -> MLE<()> {
46 println!("Disabling version {} for mod {}", versionid, mod_id); 53 //println!("Disabling version {} for mod {}", versionid, mod_id);
47 let file = get_file_path(current_list.clone(), String::from(&versionid))?; 54 let file = get_file_path(current_list.clone(), String::from(&versionid))?;
48 let disabled = format!("{}.disabled", file); 55 let disabled = format!("{}.disabled", file);
49 56
@@ -97,3 +104,13 @@ pub fn get_downloaded_versions(list: List) -> MLE<HashMap<String, String>> {
97 } 104 }
98 Ok(versions) 105 Ok(versions)
99} 106}
107
108pub fn clean_list_dir(list: &List) -> MLE<()> {
109 let dl_path = &list.download_folder;
110 println!("Clean directory for: {}", list.id);
111 for entry in std::fs::read_dir(dl_path)? {
112 let entry = entry?;
113 std::fs::remove_file(entry.path())?;
114 };
115 Ok(())
116}
diff --git a/src/input.rs b/src/input.rs
index be24660..a41f671 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -42,6 +42,7 @@ pub enum ListOptions {
42 Add, 42 Add,
43 Remove, 43 Remove,
44 Change, 44 Change,
45 Version,
45} 46}
46 47
47#[derive(Debug, Clone, PartialEq, Eq)] 48#[derive(Debug, Clone, PartialEq, Eq)]
@@ -119,14 +120,18 @@ impl Input {
119 "clean" => { 120 "clean" => {
120 clean = true; 121 clean = true;
121 }, 122 },
122 "direct-download" => { 123 "no-download" => {
123 direct_download = true; 124 direct_download = false;
124 }, 125 },
125 "delete_old" => { 126 "delete_old" => {
126 delete_old = true; 127 delete_old = true;
127 }, 128 },
128 "l" => { 129 "l" => {
129 list = Some(lists_get(config.clone(), String::from(arg_split[1]))?); 130 if arg_split.len() == 2 {
131 list = Some(lists_get(config.clone(), String::from(arg_split[1]))?);
132 } else {
133 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a list via it's id"));
134 }
130 } 135 }
131 "la" => { 136 "la" => {
132 command = Some(Cmd::List); 137 command = Some(Cmd::List);
@@ -136,18 +141,26 @@ impl Input {
136 "lr" => { 141 "lr" => {
137 command = Some(Cmd::List); 142 command = Some(Cmd::List);
138 list_options = Some(ListOptions::Remove); 143 list_options = Some(ListOptions::Remove);
139 if arg_split.len() == 2 {
140 list_id = Some(String::from(arg_split[1]));
141 list = Some(lists_get(config.clone(), list_id.clone().unwrap())?)
142 }
143 }, 144 },
144 "lc" => { 145 "lc" => {
145 command = Some(Cmd::List); 146 command = Some(Cmd::List);
146 list_options = Some(ListOptions::Change); 147 list_options = Some(ListOptions::Change);
147 list_id = Some(String::from(arg_split[1]));
148 }, 148 },
149 "lv" => { 149 "lv" => {
150 list_mcversion = Some(String::from(arg_split[1])); 150 command = Some(Cmd::List);
151 list_options = Some(ListOptions::Version);
152 if arg_split.len() == 2 {
153 list_mcversion = Some(String::from(arg_split[1]));
154 } else {
155 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a minecraft version"));
156 }
157 },
158 "mcv" => {
159 if arg_split.len() == 2 {
160 list_mcversion = Some(String::from(arg_split[1]));
161 } else {
162 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a minecraft version"));
163 }
151 }, 164 },
152 "ml" => { 165 "ml" => {
153 modloader = Some(Modloader::from(arg_split[1])?); 166 modloader = Some(Modloader::from(arg_split[1])?);
@@ -199,7 +212,7 @@ pub async fn get_input(config: Cfg, args: Vec<String>) -> MLE<Input> {
199 212
200 match input.clone().command.unwrap() { 213 match input.clone().command.unwrap() {
201 Cmd::Mod => check_mod(input, config), 214 Cmd::Mod => check_mod(input, config),
202 Cmd::List => check_list(input), 215 Cmd::List => check_list(input, config),
203 _ => Ok(input), 216 _ => Ok(input),
204 } 217 }
205} 218}
@@ -223,7 +236,7 @@ fn check_mod(mut input: Input, config: Cfg) -> MLE<Input> {
223 } 236 }
224} 237}
225 238
226fn check_list(mut input: Input) -> MLE<Input> { 239fn check_list(mut input: Input, config: Cfg) -> MLE<Input> {
227 if input.list_options.is_none() { 240 if input.list_options.is_none() {
228 return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_ARGUMENT")); 241 return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_ARGUMENT"));
229 }; 242 };
@@ -240,12 +253,19 @@ fn check_list(mut input: Input) -> MLE<Input> {
240 Ok(input) 253 Ok(input)
241 }, 254 },
242 ListOptions::Remove => { 255 ListOptions::Remove => {
243 if input.list_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "LISTS_NO_ID")); }; 256 if input.list.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_SPECIFIED")); };
244 Ok(input) 257 Ok(input)
245 }, 258 },
246 ListOptions::Change => { 259 ListOptions::Change => {
247 //TODO check if no change 260 //TODO check if no change
248 if input.list_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "LISTS_NO_ID")); }; 261 if input.list.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_SPECIFIED")); };
262 Ok(input)
263 },
264 ListOptions::Version => {
265 if input.list.is_none() {
266 println!("No list specified, using default");
267 input.list = Some(get_current_list(config)?);
268 };
249 Ok(input) 269 Ok(input)
250 } 270 }
251 } 271 }
diff --git a/src/main.rs b/src/main.rs
index 10980fb..2fca691 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -19,7 +19,7 @@ async fn main() {
19 } 19 }
20 }; 20 };
21 21
22 dbg!(&input); 22 //dbg!(&input);
23 23
24 match input.clone().command.unwrap() { 24 match input.clone().command.unwrap() {
25 Cmd::Mod => { 25 Cmd::Mod => {