diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/apis/modrinth.rs | 9 | ||||
-rw-r--r-- | src/commands/list.rs | 58 | ||||
-rw-r--r-- | src/commands/modification.rs | 31 | ||||
-rw-r--r-- | src/commands/setup.rs | 11 | ||||
-rw-r--r-- | src/commands/update.rs | 35 | ||||
-rw-r--r-- | src/db.rs | 21 | ||||
-rw-r--r-- | src/error.rs | 34 | ||||
-rw-r--r-- | src/input.rs | 140 | ||||
-rw-r--r-- | src/lib.rs | 14 |
9 files changed, 214 insertions, 139 deletions
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 | |||
128 | pub async fn project(api: String, name: &str) -> Project { | 127 | pub 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 | ||
145 | pub async fn versions(api: String, id: String, list: List) -> Vec<Version> { | 144 | pub 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 | ||
159 | pub async fn get_raw_versions(api: String, versions: Vec<String>) -> Vec<Version> { | 157 | pub 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 | |||
169 | pub fn extract_current_version(versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> { | 167 | pub 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!(×); | ||
180 | times.sort_by_key(|t| t.1); | 176 | times.sort_by_key(|t| t.1); |
181 | times.reverse(); | 177 | times.reverse(); |
182 | dbg!(×); | 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 @@ | |||
1 | use std::io::{Error, ErrorKind}; | 1 | use std::io::{Error, ErrorKind}; |
2 | 2 | ||
3 | use 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}; | 3 | use 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)] |
6 | pub struct List { | 6 | pub 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 | ||
12 | pub fn list(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> { | 13 | pub 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 | ||
44 | fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> { | 33 | fn 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 | ||
78 | fn remove(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> { | 61 | fn 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 @@ | |||
1 | use std::io::{Error, ErrorKind}; | 1 | use std::io::{Error, ErrorKind}; |
2 | 2 | ||
3 | use 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}; | 3 | use 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 | ||
5 | pub async fn modification(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> { | 5 | pub 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 @@ | |||
1 | use std::{fs::File, path::Path, io::{Error, ErrorKind}}; | 1 | use std::{fs::File, path::Path, io::{Error, ErrorKind}}; |
2 | 2 | ||
3 | use 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}; | 3 | use 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 | ||
5 | pub async fn setup(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { | 5 | pub 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 | |||
55 | fn 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 | ||
5 | use futures_util::StreamExt; | 5 | use futures_util::StreamExt; |
6 | 6 | ||
7 | use 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}; | 7 | use 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 | ||
9 | pub async fn update(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { | 9 | pub 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 = ¤t_version.mod_id; | 26 | let v_id = ¤t_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 = ¤t_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 | ||
45 | async fn specific_update(config: Cfg, list: List, project: Project) -> Result<Version, Box<dyn std::error::Error>> { | 56 | async 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 | ||
74 | async fn download_updates(config: Cfg, versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> { | 85 | async 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(¤t_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(); |
@@ -2,7 +2,7 @@ use std::io::{Error, ErrorKind}; | |||
2 | 2 | ||
3 | use rusqlite::Connection; | 3 | use rusqlite::Connection; |
4 | 4 | ||
5 | use crate::{Modloader, config::Cfg, List, get_modloader}; | 5 | use crate::{Modloader, config::Cfg, List}; |
6 | 6 | ||
7 | //mods | 7 | //mods |
8 | pub fn mods_insert(config: Cfg, id: String, name: String, versions: Vec<String>) -> Result<(), Box<dyn std::error::Error>> { | 8 | pub 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 |
229 | pub fn lists_insert(config: Cfg, id: String, mc_version: String, mod_loader: Modloader) -> Result<(), Box<dyn std::error::Error>> { | 229 | pub 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 @@ | |||
1 | use core::fmt; | ||
2 | |||
3 | pub type MLE<T> = Result<T, MLError>; | ||
4 | |||
5 | #[derive(Debug)] | ||
6 | pub struct MLError { | ||
7 | etype: ErrorType, | ||
8 | message: String, | ||
9 | } | ||
10 | |||
11 | #[derive(Debug)] | ||
12 | pub enum ErrorType { | ||
13 | ArgumentError | ||
14 | } | ||
15 | |||
16 | impl std::error::Error for MLError { | ||
17 | fn description(&self) -> &str { | ||
18 | &self.message | ||
19 | } | ||
20 | } | ||
21 | |||
22 | impl 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 | |||
30 | impl 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 @@ | |||
1 | use std::{io::{Error, ErrorKind}, env}; | 1 | use std::env; |
2 | use crate::{config::Cfg, list, modification, update, setup, download}; | 2 | use 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)] |
5 | pub struct Input { | 5 | pub 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 | ||
10 | impl Input { | 16 | impl 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)] | ||
62 | pub enum Cmd { | ||
63 | Mod, | ||
64 | List, | ||
65 | Update, | ||
66 | Download, | ||
67 | Setup | ||
68 | } | ||
69 | |||
70 | impl 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)] | ||
85 | pub enum Subcmd { | ||
86 | Add, | ||
87 | Remove, | ||
88 | Change | ||
89 | } | ||
90 | |||
91 | impl 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 | ||
34 | pub async fn get_input(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { | 103 | pub 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] |
65 | fn input_from() { | 131 | fn 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 | } |
@@ -3,6 +3,7 @@ pub mod config; | |||
3 | pub mod commands; | 3 | pub mod commands; |
4 | pub mod input; | 4 | pub mod input; |
5 | pub mod db; | 5 | pub mod db; |
6 | pub mod error; | ||
6 | 7 | ||
7 | use std::io::{Error, ErrorKind}; | 8 | use 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 { | |
28 | pub 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 | } |