summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfxqnlr <[email protected]>2022-11-17 21:20:09 +0100
committerfxqnlr <[email protected]>2022-11-17 21:20:09 +0100
commitfdd7525e5a0d298ebb8a9aa81cc19ec79e8cd113 (patch)
treeec7c7c80434b339f9442882f1e2dce6f60cc9edd
parent5145dd23f1777180d8003e76f59af57643796516 (diff)
downloadmodlist-fdd7525e5a0d298ebb8a9aa81cc19ec79e8cd113.tar
modlist-fdd7525e5a0d298ebb8a9aa81cc19ec79e8cd113.tar.gz
modlist-fdd7525e5a0d298ebb8a9aa81cc19ec79e8cd113.zip
added --clean for update && list downloadfolder
-rw-r--r--data.dbbin24576 -> 24576 bytes
-rw-r--r--planmodlist.xoppbin198243 -> 197609 bytes
-rw-r--r--src/apis/modrinth.rs9
-rw-r--r--src/commands/list.rs58
-rw-r--r--src/commands/modification.rs31
-rw-r--r--src/commands/setup.rs11
-rw-r--r--src/commands/update.rs35
-rw-r--r--src/db.rs21
-rw-r--r--src/error.rs34
-rw-r--r--src/input.rs140
-rw-r--r--src/lib.rs14
-rw-r--r--tests/db.rs5
12 files changed, 217 insertions, 141 deletions
diff --git a/data.db b/data.db
index 5308bbc..a54cd80 100644
--- a/data.db
+++ b/data.db
Binary files differ
diff --git a/planmodlist.xopp b/planmodlist.xopp
index 277f1ad..7dbf105 100644
--- a/planmodlist.xopp
+++ b/planmodlist.xopp
Binary files differ
diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs
index abb8eec..ec8d203 100644
--- a/src/apis/modrinth.rs
+++ b/src/apis/modrinth.rs
@@ -124,7 +124,6 @@ async fn get(api: String, path: String) -> Result<Vec<u8>, Box<dyn std::error::E
124 Ok(data) 124 Ok(data)
125} 125}
126 126
127
128pub async fn project(api: String, name: &str) -> Project { 127pub async fn project(api: String, name: &str) -> Project {
129 let url = format!("project/{}", name); 128 let url = format!("project/{}", name);
130 let data = get(api, url); 129 let data = get(api, url);
@@ -143,7 +142,6 @@ pub async fn projects(api: String, ids: Vec<String>) -> Vec<Project> {
143} 142}
144 143
145pub async fn versions(api: String, id: String, list: List) -> Vec<Version> { 144pub async fn versions(api: String, id: String, list: List) -> Vec<Version> {
146
147 let loaderstr = match list.modloader { 145 let loaderstr = match list.modloader {
148 Modloader::Forge => String::from("forge"), 146 Modloader::Forge => String::from("forge"),
149 Modloader::Fabric => String::from("fabric"), 147 Modloader::Fabric => String::from("fabric"),
@@ -157,7 +155,7 @@ pub async fn versions(api: String, id: String, list: List) -> Vec<Version> {
157} 155}
158 156
159pub async fn get_raw_versions(api: String, versions: Vec<String>) -> Vec<Version> { 157pub async fn get_raw_versions(api: String, versions: Vec<String>) -> Vec<Version> {
160 println!("Getting versions"); 158 println!("Getting versions {}", &versions.join(", "));
161 159
162 let url = format!(r#"versions?ids=["{}"]"#, versions.join(r#"",""#)); 160 let url = format!(r#"versions?ids=["{}"]"#, versions.join(r#"",""#));
163 161
@@ -169,18 +167,15 @@ pub async fn get_raw_versions(api: String, versions: Vec<String>) -> Vec<Version
169pub fn extract_current_version(versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> { 167pub fn extract_current_version(versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> {
170 match versions.len() { 168 match versions.len() {
171 0 => Err(Box::new(Error::new(ErrorKind::NotFound, "NO_VERSIONS_AVAILABLE"))), 169 0 => Err(Box::new(Error::new(ErrorKind::NotFound, "NO_VERSIONS_AVAILABLE"))),
172 //TODO compare publish dates
173 1.. => { 170 1.. => {
174 let mut times: Vec<(String, DateTime<FixedOffset>)> = vec![]; 171 let mut times: Vec<(String, DateTime<FixedOffset>)> = vec![];
175 for ver in versions { 172 for ver in versions {
176 let stamp = DateTime::parse_from_rfc3339(&ver.date_published)?; 173 let stamp = DateTime::parse_from_rfc3339(&ver.date_published)?;
177 times.push((ver.id, stamp)) 174 times.push((ver.id, stamp))
178 } 175 }
179 dbg!(&times);
180 times.sort_by_key(|t| t.1); 176 times.sort_by_key(|t| t.1);
181 times.reverse(); 177 times.reverse();
182 dbg!(&times); 178 println!("Current Version: {}", times[0].0);
183 println!("CW: {}", times[0].0);
184 Ok(times[0].0.to_string()) 179 Ok(times[0].0.to_string())
185 }, 180 },
186 _ => panic!("available_versions should never be negative"), 181 _ => panic!("available_versions should never be negative"),
diff --git a/src/commands/list.rs b/src/commands/list.rs
index 6c80e4e..76965df 100644
--- a/src/commands/list.rs
+++ b/src/commands/list.rs
@@ -1,38 +1,27 @@
1use std::io::{Error, ErrorKind}; 1use std::io::{Error, ErrorKind};
2 2
3use crate::{db::{lists_insert, lists_remove, config_change_current_list, lists_get_all_ids, config_get_current_list, lists_get}, Modloader, config::Cfg, input::Input}; 3use crate::{db::{lists_insert, lists_remove, config_change_current_list, lists_get_all_ids, config_get_current_list, lists_get}, Modloader, config::Cfg, input::{Input, Subcmd}};
4 4
5#[derive(Debug, Clone, PartialEq, Eq)] 5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct List { 6pub struct List {
7 pub id: String, 7 pub id: String,
8 pub mc_version: String, 8 pub mc_version: String,
9 pub modloader: Modloader, 9 pub modloader: Modloader,
10 pub download_folder: String,
10} 11}
11 12
12pub fn list(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> { 13pub fn list(config: Cfg, input: Input) -> Result<(), Box<dyn std::error::Error>> {
13 14
14 if args.is_none() { 15 match input.subcommand.ok_or("")? {
15 let lists = lists_get_all_ids(config.clone())?; 16 Subcmd::Add => {
16 let current_list = config_get_current_list(config)?; 17 add(config, input.args.ok_or("")?)
17 println!("Your lists:\n{}\n-----\nCurrently selected list: \"{}\"", lists.join(",\n"), current_list);
18 return Ok(());
19 }
20
21 let arguments = Input::from(args.unwrap().join(" "))?;
22
23 if arguments.args.is_none() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))); };
24
25 match arguments.command.as_str() {
26 "add" => {
27 add(config, arguments.args.unwrap())
28 }, 18 },
29 "change" => { 19 Subcmd::Change => {
30 change(config, arguments.args.unwrap()) 20 change(config, input.args.ok_or("")?)
31 }, 21 },
32 "remove" => { 22 Subcmd::Remove => {
33 remove(config, arguments.args.unwrap()) 23 remove(config, input.args.ok_or("")?)
34 }, 24 }
35 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_SUBCOMMAND")))
36 } 25 }
37} 26}
38 27
@@ -43,16 +32,13 @@ pub fn get_current_list(config: Cfg) -> Result<List, Box<dyn std::error::Error>>
43 32
44fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> { 33fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
45 match args.len() { 34 match args.len() {
46 1 | 2 => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))), 35 1 | 2 | 3 => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))),
47 3 => { 36 4 => {
48 let id = String::from(&args[0]); 37 let id = String::from(&args[0]);
49 let mc_version = String::from(&args[1]); 38 let mc_version = String::from(&args[1]);
50 let mod_loader = match args[2].as_str() { 39 let mod_loader = Modloader::from(&args[2])?;
51 "forge" => Modloader::Forge, 40 let download_folder = String::from(&args[3]);
52 "fabric" => Modloader::Fabric, 41 lists_insert(config, id, mc_version, mod_loader, download_folder)
53 _ => return Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_MODLOADER")))
54 };
55 lists_insert(config, id, mc_version, mod_loader)
56 }, 42 },
57 5.. => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_MANY_ARGUMENTS"))), 43 5.. => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_MANY_ARGUMENTS"))),
58 _ => panic!("list arguments should never be zero or lower"), 44 _ => panic!("list arguments should never be zero or lower"),
@@ -65,10 +51,7 @@ fn change(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Erro
65 1 => { 51 1 => {
66 let list = String::from(&args[0]); 52 let list = String::from(&args[0]);
67 if !lists.contains(&list) { return Err(Box::new(Error::new(ErrorKind::NotFound, "LIST_DOESNT_EXIST"))); }; 53 if !lists.contains(&list) { return Err(Box::new(Error::new(ErrorKind::NotFound, "LIST_DOESNT_EXIST"))); };
68 match config_change_current_list(config, list) { 54 config_change_current_list(config, list)
69 Err(..) => { Err(Box::new(Error::new(ErrorKind::Other, "72"))) },
70 Ok(()) => Ok(()),
71 }
72 }, 55 },
73 2.. => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_MANY_ARGUMENTS"))), 56 2.. => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_MANY_ARGUMENTS"))),
74 _ => panic!("list arguments should never be zero or lower"), 57 _ => panic!("list arguments should never be zero or lower"),
@@ -77,12 +60,7 @@ fn change(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Erro
77 60
78fn remove(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> { 61fn remove(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
79 match args.len() { 62 match args.len() {
80 1 => { 63 1 => lists_remove(config, String::from(&args[0])),
81 match lists_remove(config, String::from(&args[0])) {
82 Err(..) => { Err(Box::new(Error::new(ErrorKind::Other, "85"))) },
83 Ok(()) => Ok(()),
84 }
85 },
86 2.. => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_MANY_ARGUMENTS"))), 64 2.. => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_MANY_ARGUMENTS"))),
87 _ => panic!("list arguments should never be zero or lower"), 65 _ => panic!("list arguments should never be zero or lower"),
88 } 66 }
diff --git a/src/commands/modification.rs b/src/commands/modification.rs
index 595b677..519a0cb 100644
--- a/src/commands/modification.rs
+++ b/src/commands/modification.rs
@@ -1,23 +1,17 @@
1use std::io::{Error, ErrorKind}; 1use std::io::{Error, ErrorKind};
2 2
3use crate::{modrinth::{project, versions, extract_current_version}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, mods_get_all_ids, userlist_get_all_ids}, input::Input, get_current_list}; 3use crate::{modrinth::{project, versions, extract_current_version}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, mods_get_all_ids, userlist_get_all_ids}, input::{Input, Subcmd}, get_current_list};
4 4
5pub async fn modification(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> { 5pub async fn modification(config: Cfg, input: Input) -> Result<(), Box<dyn std::error::Error>> {
6 6
7 if args.is_none() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))) } 7 match input.subcommand.ok_or("")? {
8 8 Subcmd::Add => {
9 let arguments = Input::from(args.unwrap().join(" "))?; 9 add(config, input.args.ok_or("")?).await
10
11 if arguments.args.is_none() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))); };
12
13 match arguments.command.as_str() {
14 "add" => {
15 add(config, arguments.args.unwrap()).await
16 }, 10 },
17 "remove" => { 11 Subcmd::Remove => {
18 remove(config, arguments.args.unwrap()) 12 remove(config, input.args.ok_or("")?)
19 }, 13 },
20 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_SUBCOMMAND"))) 14 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "SUBCOMMAND_NOT_AVAILABLE")))
21 } 15 }
22} 16}
23 17
@@ -29,8 +23,6 @@ async fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::E
29 23
30 let project = project(String::from(&config.apis.modrinth), &args[0]).await; 24 let project = project(String::from(&config.apis.modrinth), &args[0]).await;
31 25
32 dbg!(&project);
33
34 if project.versions.is_empty() { panic!("This should never happen"); }; 26 if project.versions.is_empty() { panic!("This should never happen"); };
35 27
36 let available_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), current_list.clone()).await; 28 let available_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), current_list.clone()).await;
@@ -48,7 +40,6 @@ async fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::E
48 //add to current list and mod table 40 //add to current list and mod table
49 match userlist_get_all_ids(config.clone(), current_list.clone().id) { 41 match userlist_get_all_ids(config.clone(), current_list.clone().id) {
50 Ok(mods) => { 42 Ok(mods) => {
51 dbg!(&mods);
52 if mods.contains(&project.id) { 43 if mods.contains(&project.id) {
53 return Err(Box::new(Error::new(ErrorKind::Other, "MOD_ALREADY_ON_LIST"))); } 44 return Err(Box::new(Error::new(ErrorKind::Other, "MOD_ALREADY_ON_LIST"))); }
54 else { 45 else {
@@ -60,7 +51,6 @@ async fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::E
60 51
61 match mods_get_all_ids(config.clone()) { 52 match mods_get_all_ids(config.clone()) {
62 Ok(mods) => { 53 Ok(mods) => {
63 dbg!(&mods);
64 if mods.contains(&project.id) { 54 if mods.contains(&project.id) {
65 return Err(Box::new(Error::new(ErrorKind::Other, "MOD_ALREADY_IN_DATABASE"))) 55 return Err(Box::new(Error::new(ErrorKind::Other, "MOD_ALREADY_IN_DATABASE")))
66 } else { 56 } else {
@@ -81,8 +71,5 @@ fn remove(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Erro
81 let mod_id = mods_get_id(config.clone(), String::from(&args[0]))?; 71 let mod_id = mods_get_id(config.clone(), String::from(&args[0]))?;
82 72
83 //TODO implement remove from modlist if not in any other lists && config clean is true 73 //TODO implement remove from modlist if not in any other lists && config clean is true
84 match userlist_remove(config, current_list.id, mod_id) { 74 userlist_remove(config, current_list.id, mod_id)
85 Err(..) => { Err(Box::new(Error::new(ErrorKind::Other, "TBD"))) },
86 Ok(()) => Ok(()),
87 }
88} 75}
diff --git a/src/commands/setup.rs b/src/commands/setup.rs
index 8c0fcfd..be06040 100644
--- a/src/commands/setup.rs
+++ b/src/commands/setup.rs
@@ -1,9 +1,8 @@
1use std::{fs::File, path::Path, io::{Error, ErrorKind}}; 1use std::{fs::File, path::Path, io::{Error, ErrorKind}};
2 2
3use crate::{config::Cfg, db::{db_setup, s_config_get_version, s_config_create_version, s_insert_column, lists_get_all_ids, lists_get, userlist_get_all_current_version_ids, s_userlist_update_download}, modrinth::get_raw_versions}; 3use crate::{config::Cfg, db::{db_setup, s_config_get_version, s_config_create_version, s_insert_column, lists_get_all_ids, lists_get, userlist_get_all_current_version_ids, s_userlist_update_download, s_config_update_version}, modrinth::get_raw_versions};
4 4
5pub async fn setup(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { 5pub async fn setup(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
6
7 let db_file = format!("{}/data.db", String::from(&config.data)); 6 let db_file = format!("{}/data.db", String::from(&config.data));
8 7
9 if !Path::new(&db_file).exists() { 8 if !Path::new(&db_file).exists() {
@@ -13,6 +12,7 @@ pub async fn setup(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
13 match s_config_get_version(config.clone()) { 12 match s_config_get_version(config.clone()) {
14 Ok(ver) => { 13 Ok(ver) => {
15 match ver.as_str() { 14 match ver.as_str() {
15 "0.2" => to_03(config)?,
16 _ => return Err(Box::new(Error::new(ErrorKind::Other, "UNKNOWN_VERSION"))) 16 _ => return Err(Box::new(Error::new(ErrorKind::Other, "UNKNOWN_VERSION")))
17 } 17 }
18 }, 18 },
@@ -33,7 +33,7 @@ async fn to_02(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
33 33
34 for list in lists { 34 for list in lists {
35 println!("Updating {}", list); 35 println!("Updating {}", list);
36 s_insert_column(config.clone(), String::from(&list), String::from("current_download"), String::new())?; 36 s_insert_column(config.clone(), String::from(&list), String::from("current_download"), String::from("TEXT"))?;
37 37
38 let full_list = lists_get(config.clone(), String::from(&list))?; 38 let full_list = lists_get(config.clone(), String::from(&list))?;
39 39
@@ -51,3 +51,8 @@ async fn to_02(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
51 51
52 Ok(()) 52 Ok(())
53} 53}
54
55fn to_03(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
56 s_insert_column(config.clone(), String::from("lists"), String::from("download_folder"), String::from("TEXT"))?;
57 s_config_update_version(config, String::from("0.3"))
58}
diff --git a/src/commands/update.rs b/src/commands/update.rs
index e383eae..85630f5 100644
--- a/src/commands/update.rs
+++ b/src/commands/update.rs
@@ -4,9 +4,9 @@ use reqwest::Client;
4 4
5use futures_util::StreamExt; 5use futures_util::StreamExt;
6 6
7use 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}, List}; 7use 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}, List, input::Input};
8 8
9pub async fn update(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { 9pub async fn update(config: Cfg, input: Input) -> Result<(), Box<dyn std::error::Error>> {
10 10
11 let current_list = get_current_list(config.clone())?; 11 let current_list = get_current_list(config.clone())?;
12 12
@@ -20,15 +20,16 @@ pub async fn update(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
20 20
21 let mut updatestack: Vec<Version> = vec![]; 21 let mut updatestack: Vec<Version> = vec![];
22 for (index, project) in projects.into_iter().enumerate() { 22 for (index, project) in projects.into_iter().enumerate() {
23 //Get versions for project and check if they match up
23 let current_version = &versions[index]; 24 let current_version = &versions[index];
24
25 let p_id = String::from(&project.id); 25 let p_id = String::from(&project.id);
26 let v_id = &current_version.mod_id; 26 let v_id = &current_version.mod_id;
27
28 if &p_id != v_id { return Err(Box::new(Error::new(ErrorKind::Other, "SORTING_ERROR"))) }; 27 if &p_id != v_id { return Err(Box::new(Error::new(ErrorKind::Other, "SORTING_ERROR"))) };
29 28
30 if project.versions.join("|") != current_version.versions { 29
31 updatestack.push(match specific_update(config.clone(), current_list.clone(), project).await { 30 //Adding to stack if not the same versions in the list OR if clean == true
31 if input.clone().clean || (project.versions.join("|") != current_version.versions) {
32 updatestack.push(match specific_update(config.clone(), input.clone(), current_list.clone(), project).await {
32 Ok(ver) => ver, 33 Ok(ver) => ver,
33 //TODO handle errors (only continue on "NO_UPDATE_AVAILABLE") 34 //TODO handle errors (only continue on "NO_UPDATE_AVAILABLE")
34 Err(_) => { continue; }, 35 Err(_) => { continue; },
@@ -36,13 +37,23 @@ pub async fn update(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
36 }; 37 };
37 }; 38 };
38 //println!("{:?}", updatestack); 39 //println!("{:?}", updatestack);
40
41
42 if input.clean {
43 let dl_path = &current_list.download_folder;
44 println!("Cleaning {}", dl_path);
45 for entry in std::fs::read_dir(dl_path)? {
46 let entry = entry?;
47 std::fs::remove_file(entry.path())?;
48 }
49 }
39 50
40 download_updates(config, updatestack).await?; 51 download_updates(config, current_list, updatestack).await?;
41 52
42 Ok(()) 53 Ok(())
43} 54}
44 55
45async fn specific_update(config: Cfg, list: List, project: Project) -> Result<Version, Box<dyn std::error::Error>> { 56async fn specific_update(config: Cfg, input: Input, list: List, project: Project) -> Result<Version, Box<dyn std::error::Error>> {
46 print!("Checking update for '{}' in {}", project.title, list.id); 57 print!("Checking update for '{}' in {}", project.title, list.id);
47 58
48 let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await; 59 let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await;
@@ -54,7 +65,7 @@ async fn specific_update(config: Cfg, list: List, project: Project) -> Result<Ve
54 } 65 }
55 66
56 let mut current: Vec<Version> = vec![]; 67 let mut current: Vec<Version> = vec![];
57 if versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&project.id))? { 68 if input.clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&project.id))?) {
58 //get new versions 69 //get new versions
59 print!(" | getting new version"); 70 print!(" | getting new version");
60 let current_str = extract_current_version(applicable_versions.clone())?; 71 let current_str = extract_current_version(applicable_versions.clone())?;
@@ -71,9 +82,9 @@ async fn specific_update(config: Cfg, list: List, project: Project) -> Result<Ve
71 Ok(current[0].clone()) 82 Ok(current[0].clone())
72} 83}
73 84
74async fn download_updates(config: Cfg, versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> { 85async fn download_updates(config: Cfg, current_list: List, versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> {
75 86
76 let dl_path = String::from(&config.downloads); 87 let dl_path = String::from(&current_list.download_folder);
77 88
78 for ver in versions { 89 for ver in versions {
79 let primary_file = ver.files.into_iter().find(|file| file.primary).unwrap(); 90 let primary_file = ver.files.into_iter().find(|file| file.primary).unwrap();
diff --git a/src/db.rs b/src/db.rs
index 5d82271..6b1e3ab 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -2,7 +2,7 @@ use std::io::{Error, ErrorKind};
2 2
3use rusqlite::Connection; 3use rusqlite::Connection;
4 4
5use crate::{Modloader, config::Cfg, List, get_modloader}; 5use crate::{Modloader, config::Cfg, List};
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>) -> Result<(), Box<dyn std::error::Error>> {
@@ -226,14 +226,14 @@ pub fn userlist_get_all_downloads(config: Cfg, list_id: String) -> Result<Vec<St
226} 226}
227 227
228//lists 228//lists
229pub fn lists_insert(config: Cfg, id: String, mc_version: String, mod_loader: Modloader) -> Result<(), Box<dyn std::error::Error>> { 229pub fn lists_insert(config: Cfg, id: String, mc_version: String, mod_loader: Modloader, download_folder: String) -> Result<(), Box<dyn std::error::Error>> {
230 println!("Creating list {}", id); 230 println!("Creating list {}", id);
231 231
232 let data = format!("{}/data.db", config.data); 232 let data = format!("{}/data.db", config.data);
233 let connection = Connection::open(data)?; 233 let connection = Connection::open(data)?;
234 234
235 connection.execute("INSERT INTO lists VALUES (?1, ?2, ?3)", [id.clone(), mc_version, mod_loader.stringify()])?; 235 connection.execute("INSERT INTO lists VALUES (?1, ?2, ?3, ?4)", [id.clone(), mc_version, mod_loader.stringify(), download_folder])?;
236 connection.execute(format!("CREATE TABLE {}( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'current_download' TEXT)", id).as_str(), [])?; 236 connection.execute(format!("CREATE TABLE {}( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'current_download' TEXT )", id).as_str(), [])?;
237 237
238 Ok(()) 238 Ok(())
239} 239}
@@ -251,16 +251,16 @@ pub fn lists_get(config: Cfg, list_id: String) -> Result<List, Box<dyn std::erro
251 let data = format!("{}/data.db", config.data); 251 let data = format!("{}/data.db", config.data);
252 let connection = Connection::open(data).unwrap(); 252 let connection = Connection::open(data).unwrap();
253 253
254 let mut list = List { id: String::new(), mc_version: String::new(), modloader: Modloader::Fabric }; 254 let mut list = List { id: String::new(), mc_version: String::new(), modloader: Modloader::Fabric, download_folder: String::new() };
255 let mut stmt = connection.prepare("SELECT mc_version, modloader FROM lists WHERE id = ?")?; 255 let mut stmt = connection.prepare("SELECT mc_version, modloader, download_folder FROM lists WHERE id = ?")?;
256 256
257 let list_iter = stmt.query_map([&list_id], |row| { 257 let list_iter = stmt.query_map([&list_id], |row| {
258 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?]) 258 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?, row.get::<usize, String>(2)?])
259 })?; 259 })?;
260 260
261 for l in list_iter { 261 for l in list_iter {
262 let li = l?; 262 let li = l?;
263 list = List { id: String::from(&list_id), mc_version: String::from(&li[0]), modloader: get_modloader(String::from(&li[1]))? }; 263 list = List { id: String::from(&list_id), mc_version: String::from(&li[0]), modloader: Modloader::from(&li[1])?, download_folder: String::from(&li[2]) };
264 }; 264 };
265 265
266 if list.id.is_empty() { return Err(Box::new(Error::new(ErrorKind::Other, "LIST_NOT_FOUND"))); } 266 if list.id.is_empty() { return Err(Box::new(Error::new(ErrorKind::Other, "LIST_NOT_FOUND"))); }
@@ -378,11 +378,10 @@ pub fn db_setup(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
378 connection.execute_batch( 378 connection.execute_batch(
379 "CREATE TABLE 'user_config' ( 'id' TEXT, 'value' TEXT ); 379 "CREATE TABLE 'user_config' ( 'id' TEXT, 'value' TEXT );
380 CREATE TABLE 'mods' ( 'id' TEXT, 'name' TEXT, 'versions' TEXT ); 380 CREATE TABLE 'mods' ( 'id' TEXT, 'name' TEXT, 'versions' TEXT );
381 CREATE TABLE 'lists' ( 'id' TEXT, 'mc_version' TEXT, 'modloader' TEXT ); 381 CREATE TABLE 'lists' ( 'id' TEXT, 'mc_version' TEXT, 'modloader' TEXT, 'download_folder' TEXT );
382 INSERT INTO 'user_config' VALUES ( 'db_version', '0.2' ); 382 INSERT INTO 'user_config' VALUES ( 'db_version', '0.3' );
383 INSERT INTO 'user_config' VALUES ( 'current_list', '...' )", 383 INSERT INTO 'user_config' VALUES ( 'current_list', '...' )",
384 )?; 384 )?;
385 385
386 Ok(()) 386 Ok(())
387} 387}
388
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..c82688c
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,34 @@
1use core::fmt;
2
3pub type MLE<T> = Result<T, MLError>;
4
5#[derive(Debug)]
6pub struct MLError {
7 etype: ErrorType,
8 message: String,
9}
10
11#[derive(Debug)]
12pub enum ErrorType {
13 ArgumentError
14}
15
16impl std::error::Error for MLError {
17 fn description(&self) -> &str {
18 &self.message
19 }
20}
21
22impl fmt::Display for MLError {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 match self.etype {
25 ErrorType::ArgumentError => write!(f, "ARGS")
26 }
27 }
28}
29
30impl MLError {
31 pub fn new(etype: ErrorType, message: &str) -> Self {
32 Self { etype, message: String::from(message) }
33 }
34}
diff --git a/src/input.rs b/src/input.rs
index c7e82d9..109fa0c 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -1,69 +1,135 @@
1use std::{io::{Error, ErrorKind}, env}; 1use std::env;
2use crate::{config::Cfg, list, modification, update, setup, download}; 2use crate::{config::Cfg, list, modification, update, setup, download, error::{MLError, ErrorType, MLE}};
3 3
4#[derive(Debug, PartialEq, Eq)] 4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct Input { 5pub struct Input {
6 pub command: String, 6 pub command: Cmd,
7 pub subcommand: Option<Subcmd>,
7 pub args: Option<Vec<String>>, 8 pub args: Option<Vec<String>>,
9 pub direct_download: bool,
10 pub force_download: bool,
11 pub all_lists: bool,
12 pub delete_old: bool,
13 pub clean: bool,
8} 14}
9 15
10impl Input { 16impl Input {
11 pub fn from(string: String) -> Result<Self, Box<dyn std::error::Error>> { 17 pub fn from(string: &str) -> MLE<Self> {
12 let mut split: Vec<&str> = string.split(' ').collect(); 18 let mut split: Vec<&str> = string.split(' ').collect();
13 let command: String; 19
14 let mut args: Option<Vec<String>> = None; 20 let mut direct_download = false;
15 if split[0].is_empty() { split.remove(0); }; 21 let mut force_download = false;
16 match split.len() { 22 let mut all_lists = false;
17 0 => { Err(Box::new(Error::new(ErrorKind::InvalidInput, "NO_ARGS"))) } 23 let mut delete_old = false;
18 1 => Ok( Input { command: split[0].to_string(), args }), 24 let mut clean = false;
19 2.. => { 25
20 command = split[0].to_string(); 26 for (i, input) in split.clone().into_iter().enumerate() {
21 split.remove(0); 27 if input.starts_with("--") {
22 let mut str_args: Vec<String> = vec![]; 28 match input {
23 for e in split { 29 "--direct-download" => direct_download = true,
24 str_args.push(e.to_string()); 30 "--force-download" => force_download = true,
31 "--all_lists" => all_lists = true,
32 "--delete_old" => delete_old = true,
33 "--clean" => clean = true,
34 _ => continue,
25 } 35 }
26 args = Some(str_args); 36 split.remove(i);
27 Ok(Input { command, args }) 37 }
28 },
29 _ => { panic!("This should never happen") }
30 } 38 }
39
40 let command = Cmd::from(split.remove(0))?;
41 let subcommand = match split.is_empty() {
42 false => Some(Subcmd::from(split.remove(0))?),
43 true => None
44 };
45
46 let args = match split.is_empty() {
47 true => None,
48 false => {
49 let mut strsplit: Vec<String> = Vec::new();
50 for s in split {
51 strsplit.push(String::from(s))
52 }
53 Some(strsplit)
54 }
55 };
56
57 Ok(Self { command, subcommand, args, direct_download, force_download, all_lists, delete_old, clean })
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub enum Cmd {
63 Mod,
64 List,
65 Update,
66 Download,
67 Setup
68}
69
70impl Cmd {
71 pub fn from(string: &str) -> MLE<Self> {
72 let cmd = match string {
73 "mod" => Self::Mod,
74 "list" => Self::List,
75 "update" => Self::Update,
76 "download" => Self::Download,
77 "setup" => Self::Setup,
78 _ => return Err(MLError::new(ErrorType::ArgumentError, "Unknown command"))
79 };
80 Ok(cmd)
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub enum Subcmd {
86 Add,
87 Remove,
88 Change
89}
90
91impl Subcmd {
92 fn from(string: &str) -> MLE<Self> {
93 let cmd = match string {
94 "add" => Self::Add,
95 "remove" => Self::Remove,
96 "change" => Self::Change,
97 _ => return Err(MLError::new(ErrorType::ArgumentError, "SUBCMD_NOT_FOUND"))
98 };
99 Ok(cmd)
31 } 100 }
32} 101}
33 102
34pub async fn get_input(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { 103pub async fn get_input(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
35 let mut args: Vec<String> = env::args().collect(); 104 let mut args: Vec<String> = env::args().collect();
36 dbg!(&args);
37 args.reverse(); 105 args.reverse();
38 args.pop(); 106 args.pop();
39 args.reverse(); 107 args.reverse();
40 dbg!(&args);
41 108
42 let input = Input::from(args.join(" "))?; 109 let input = Input::from(&args.join(" "))?;
43 110
44 match input.command.as_str() { 111 match input.command {
45 "mod" => { 112 Cmd::Mod => {
46 modification(config, input.args).await 113 modification(config, input).await
47 }, 114 },
48 "list" => { 115 Cmd::List => {
49 list(config, input.args) 116 list(config, input)
50 }, 117 },
51 "update" => { 118 Cmd::Update => {
52 update(config).await 119 update(config, input).await
53 }, 120 },
54 "setup" => { 121 Cmd::Setup => {
55 setup(config).await 122 setup(config).await
56 }, 123 },
57 "download" => { 124 Cmd::Download => {
58 download(config).await 125 download(config).await
59 }, 126 }
60 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_COMMAND"))),
61 } 127 }
62} 128}
63 129
64#[test] 130#[test]
65fn input_from() { 131fn input_from() {
66 let string = String::from("list add test 1.19.2 fabric"); 132 let string = "lis add test 1.19.2 fabric";
67 let input = Input { command: String::from("list"), args: Some(vec![String::from("add"), String::from("test"), String::from("1.19.2"), String::from("fabric")]) }; 133 let input = Input{ command: Cmd::List, subcommand: Some(Subcmd::Add), args: Some(vec![String::from("test"), String::from("1.19.2"), String::from("fabric")]), force_download: false, direct_download: false, all_lists: false };
68 assert_eq!(Input::from(string).unwrap(), input); 134 assert_eq!(Input::from(string).unwrap(), input);
69} 135}
diff --git a/src/lib.rs b/src/lib.rs
index 1e7ebbf..51b4487 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,6 +3,7 @@ pub mod config;
3pub mod commands; 3pub mod commands;
4pub mod input; 4pub mod input;
5pub mod db; 5pub mod db;
6pub mod error;
6 7
7use std::io::{Error, ErrorKind}; 8use std::io::{Error, ErrorKind};
8 9
@@ -23,12 +24,11 @@ impl Modloader {
23 } 24 }
24 } 25 }
25 26
26} 27 fn from(string: &str) -> Result<Modloader, Box<Error>> {
27 28 match string {
28pub fn get_modloader(string: String) -> Result<Modloader, Box<dyn std::error::Error>> { 29 "forge" => Ok(Modloader::Forge),
29 match string.as_str() { 30 "fabric" => Ok(Modloader::Fabric),
30 "forge" => Ok(Modloader::Forge), 31 _ => Err(Box::new(Error::new(ErrorKind::InvalidData, "UNKNOWN_MODLOADER")))
31 "fabric" => Ok(Modloader::Fabric), 32 }
32 _ => Err(Box::new(Error::new(ErrorKind::InvalidData, "UNKNOWN_MODLOADER")))
33 } 33 }
34} 34}
diff --git a/tests/db.rs b/tests/db.rs
index 16061d0..7127fc6 100644
--- a/tests/db.rs
+++ b/tests/db.rs
@@ -1,4 +1,4 @@
1use std::{fs::{File, create_dir_all}, path::Path, sync::Once}; 1use std::{fs::{File, create_dir_all, remove_dir_all}, path::Path, sync::Once};
2 2
3use modlist::{config::{Cfg, Apis}, db::{mods_insert, db_setup, mods_get_all_ids, mods_get_id, mods_remove, userlist_insert, lists_insert, userlist_get_all_ids, userlist_remove, mods_get_versions, userlist_get_applicable_versions, lists_remove, lists_get, lists_get_all_ids, userlist_get_all_current_version_ids, userlist_change_versions, s_userlist_update_download, userlist_get_all_downloads, config_change_current_list, config_get_current_list, s_config_update_version, s_config_create_version, s_config_get_version}, Modloader, List}; 3use modlist::{config::{Cfg, Apis}, db::{mods_insert, db_setup, mods_get_all_ids, mods_get_id, mods_remove, userlist_insert, lists_insert, userlist_get_all_ids, userlist_remove, mods_get_versions, userlist_get_applicable_versions, lists_remove, lists_get, lists_get_all_ids, userlist_get_all_current_version_ids, userlist_change_versions, s_userlist_update_download, userlist_get_all_downloads, config_change_current_list, config_get_current_list, s_config_update_version, s_config_create_version, s_config_get_version}, Modloader, List};
4 4
@@ -11,6 +11,7 @@ fn setup() -> Cfg {
11 11
12 INIT.call_once(|| { 12 INIT.call_once(|| {
13 let db_path = Path::new(db_pathstr); 13 let db_path = Path::new(db_pathstr);
14 if db_path.exists() { remove_dir_all(db_pathstr).unwrap(); };
14 create_dir_all(db_path).unwrap(); 15 create_dir_all(db_path).unwrap();
15 let db_filestr = format!("{}/data.db", db_pathstr); 16 let db_filestr = format!("{}/data.db", db_pathstr);
16 File::create(db_filestr).unwrap(); 17 File::create(db_filestr).unwrap();
@@ -46,7 +47,7 @@ fn test_mods_get_all_ids() {
46fn test_mods_get_id() { 47fn test_mods_get_id() {
47 let config = setup(); 48 let config = setup();
48 49
49 mods_insert(config.clone(), String::from("GI"), String::from("GETID_TEST"), vec![String::from("GI_VER1"), String::from("GI_VER2")]).unwrap(); 50 mods_insert(config, String::from("GI"), String::from("GETID_TEST"), vec![String::from("GI_VER1"), String::from("GI_VER2")]).unwrap();
50} 51}
51 52
52#[test] 53#[test]