summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfxqnlr <[email protected]>2022-11-03 21:34:04 +0100
committerfxqnlr <[email protected]>2022-11-03 21:34:04 +0100
commit96cc5257de09682df345e768dc2a91303f9b36c9 (patch)
treef505d14c581e2bef4cfe222bd1069661bedd22e0
parentb125dfd03084fff47ab8e90d002c6699b762d998 (diff)
downloadmodlist-96cc5257de09682df345e768dc2a91303f9b36c9.tar
modlist-96cc5257de09682df345e768dc2a91303f9b36c9.tar.gz
modlist-96cc5257de09682df345e768dc2a91303f9b36c9.zip
added update beginnings; init of tests
-rw-r--r--config.ini1
-rw-r--r--data.dbbin36864 -> 36864 bytes
-rw-r--r--src/apis/modrinth.rs18
-rw-r--r--src/commands/add.rs52
-rw-r--r--src/commands/list.rs16
-rw-r--r--src/commands/mod.rs6
-rw-r--r--src/commands/modification.rs88
-rw-r--r--src/commands/update.rs40
-rw-r--r--src/config.rs3
-rw-r--r--src/db.rs183
-rw-r--r--src/input.rs12
-rw-r--r--src/lib.rs22
-rw-r--r--tests/db/mod.rs5
-rw-r--r--tests/db_integration.rs7
14 files changed, 363 insertions, 90 deletions
diff --git a/config.ini b/config.ini
index ce33108..0ffe0ac 100644
--- a/config.ini
+++ b/config.ini
@@ -1,4 +1,5 @@
1data = "./" 1data = "./"
2clean_remove = false
2 3
3[apis] 4[apis]
4modrinth = "http://localhost:8080/" 5modrinth = "http://localhost:8080/"
diff --git a/data.db b/data.db
index 60431f1..73464d8 100644
--- a/data.db
+++ b/data.db
Binary files differ
diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs
index 3af5bbd..0c3eca5 100644
--- a/src/apis/modrinth.rs
+++ b/src/apis/modrinth.rs
@@ -1,6 +1,6 @@
1use serde::Deserialize; 1use serde::Deserialize;
2 2
3use crate::Modloader; 3use crate::{Modloader, List};
4 4
5#[derive(Debug, Deserialize)] 5#[derive(Debug, Deserialize)]
6pub struct Project { 6pub struct Project {
@@ -68,7 +68,7 @@ pub enum Status {
68 unknown 68 unknown
69} 69}
70 70
71#[derive(Debug, Deserialize)] 71#[derive(Debug, Clone, Deserialize)]
72pub struct Version { 72pub struct Version {
73 pub name: String, 73 pub name: String,
74 pub version_number: String, 74 pub version_number: String,
@@ -86,14 +86,14 @@ pub struct Version {
86} 86}
87 87
88#[allow(non_camel_case_types)] 88#[allow(non_camel_case_types)]
89#[derive(Debug, Deserialize)] 89#[derive(Debug, Clone, Deserialize)]
90pub enum VersionType { 90pub enum VersionType {
91 release, 91 release,
92 beta, 92 beta,
93 alpha 93 alpha
94} 94}
95 95
96#[derive(Debug, Deserialize)] 96#[derive(Debug, Clone, Deserialize)]
97pub struct VersionFile { 97pub struct VersionFile {
98 pub hashes: Hash, 98 pub hashes: Hash,
99 pub url: String, 99 pub url: String,
@@ -102,7 +102,7 @@ pub struct VersionFile {
102 pub size: u32, 102 pub size: u32,
103} 103}
104 104
105#[derive(Debug, Deserialize)] 105#[derive(Debug, Clone, Deserialize)]
106pub struct Hash { 106pub struct Hash {
107 pub sha512: String, 107 pub sha512: String,
108 pub sha1: String, 108 pub sha1: String,
@@ -130,7 +130,7 @@ pub async fn project(api: String, name: &str) -> Project {
130 serde_json::from_slice(&data.await.unwrap()).unwrap() 130 serde_json::from_slice(&data.await.unwrap()).unwrap()
131} 131}
132 132
133pub async fn projects(api: String, ids: Vec<&str>) -> Vec<Project> { 133pub async fn projects(api: String, ids: Vec<String>) -> Vec<Project> {
134 let all = ids.join(r#"",""#); 134 let all = ids.join(r#"",""#);
135 let url = format!(r#"projects?ids=["{}"]"#, all); 135 let url = format!(r#"projects?ids=["{}"]"#, all);
136 println!("{}", url); 136 println!("{}", url);
@@ -140,14 +140,14 @@ pub async fn projects(api: String, ids: Vec<&str>) -> Vec<Project> {
140 serde_json::from_slice(&data.await.unwrap()).unwrap() 140 serde_json::from_slice(&data.await.unwrap()).unwrap()
141} 141}
142 142
143pub async fn versions(api: String, id: String, loader: Modloader, mc_version: String) -> Vec<Version> { 143pub async fn versions(api: String, id: String, list: List) -> Vec<Version> {
144 144
145 let loaderstr = match loader { 145 let loaderstr = match list.modloader {
146 Modloader::Forge => String::from("forge"), 146 Modloader::Forge => String::from("forge"),
147 Modloader::Fabric => String::from("fabric"), 147 Modloader::Fabric => String::from("fabric"),
148 }; 148 };
149 149
150 let url = format!(r#"project/{}/version?loaders=["{}"]&game_versions=["{}"]"#, id, loaderstr, mc_version); 150 let url = format!(r#"project/{}/version?loaders=["{}"]&game_versions=["{}"]"#, id, loaderstr, list.mc_version);
151 151
152 let data = get(api, url); 152 let data = get(api, url);
153 153
diff --git a/src/commands/add.rs b/src/commands/add.rs
deleted file mode 100644
index ed4a6d8..0000000
--- a/src/commands/add.rs
+++ /dev/null
@@ -1,52 +0,0 @@
1use std::io::{Error, ErrorKind};
2
3use crate::{modrinth::{project, versions}, config::Cfg, db::insert_mod, Modloader, input::Input};
4
5pub async fn modification(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> {
6
7 if args.is_none() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))) }
8
9 let arguments = Input::from(args.unwrap().join(" "))?;
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 },
17 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_SUBCOMMAND")))
18 }
19}
20
21pub async fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
22
23 if args.len() < 1 { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))); };
24
25 let project = project(String::from(&config.apis.modrinth), &args[0]).await;
26
27 dbg!(&project);
28
29 let loader = Modloader::Fabric;
30
31 if project.versions.is_empty() { panic!("This should never happen"); };
32
33 let current_version = get_current(config, String::from(&project.id)).await?;
34
35 //add to current list and mod table
36 match insert_mod(project.id, project.title, current_version, project.versions, loader, String::from("1.19.2")) {
37 Err(err) => { Err(Box::new(err)) },
38 Ok(()) => Ok(()),
39 }
40
41}
42
43async fn get_current(config: Cfg, id: String) -> Result<String, Box<dyn std::error::Error>> {
44 let available_versions = versions(config.apis.modrinth, id, Modloader::Fabric, String::from("1.19.2")).await;
45
46 match available_versions.len() {
47 0 => Err(Box::new(Error::new(ErrorKind::NotFound, "NO_VERSIONS_AVAILABLE"))),
48 //TODO compare publish dates
49 1.. => Ok(available_versions[0].id.to_string()),
50 _ => panic!("available_versions should never be negative"),
51 }
52}
diff --git a/src/commands/list.rs b/src/commands/list.rs
index 6c260ce..3dfe1ad 100644
--- a/src/commands/list.rs
+++ b/src/commands/list.rs
@@ -1,12 +1,19 @@
1use std::io::{Error, ErrorKind}; 1use std::io::{Error, ErrorKind};
2 2
3use crate::{db::{insert_list, remove_list, change_list, get_lists, get_current_list}, Modloader, config::Cfg, input::Input}; 3use crate::{db::{insert_list, remove_list, change_list, get_lists, get_current_list_id, get_list}, Modloader, config::Cfg, input::Input};
4
5#[derive(Clone)]
6pub struct List {
7 pub id: String,
8 pub mc_version: String,
9 pub modloader: Modloader,
10}
4 11
5pub fn list(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> { 12pub fn list(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> {
6 13
7 if args.is_none() { 14 if args.is_none() {
8 let lists = get_lists(config.clone())?; 15 let lists = get_lists(config.clone())?;
9 let current_list = get_current_list(config)?; 16 let current_list = get_current_list_id(config)?;
10 println!("Your lists:\n{}\n-----\nCurrently selected list: \"{}\"", lists.join(",\n"), current_list); 17 println!("Your lists:\n{}\n-----\nCurrently selected list: \"{}\"", lists.join(",\n"), current_list);
11 return Ok(()); 18 return Ok(());
12 } 19 }
@@ -29,6 +36,11 @@ pub fn list(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::e
29 } 36 }
30} 37}
31 38
39pub fn get_current_list(config: Cfg) -> Result<List, Box<dyn std::error::Error>> {
40 let id = get_current_list_id(config.clone())?;
41 get_list(config, id)
42}
43
32fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> { 44fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
33 match args.len() { 45 match args.len() {
34 1 | 2 => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))), 46 1 | 2 => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))),
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 6432746..5d008fd 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -1,5 +1,7 @@
1pub mod add; 1pub mod modification;
2pub mod list; 2pub mod list;
3pub mod update;
3 4
4pub use add::*; 5pub use modification::*;
5pub use list::*; 6pub use list::*;
7pub use update::*;
diff --git a/src/commands/modification.rs b/src/commands/modification.rs
new file mode 100644
index 0000000..43e2180
--- /dev/null
+++ b/src/commands/modification.rs
@@ -0,0 +1,88 @@
1use std::io::{Error, ErrorKind};
2
3use crate::{modrinth::{project, versions, Version}, config::Cfg, db::{insert_mod, remove_mod_from_list, get_mod_id, insert_mod_in_list, get_mods, get_mods_from_list}, input::Input, get_current_list};
4
5pub async fn modification(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> {
6
7 if args.is_none() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))) }
8
9 let arguments = Input::from(args.unwrap().join(" "))?;
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 },
17 "remove" => {
18 remove(config, arguments.args.unwrap())
19 },
20 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_SUBCOMMAND")))
21 }
22}
23
24async fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
25
26 if args.is_empty() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))); };
27
28 let current_list = get_current_list(config.clone())?;
29
30 let project = project(String::from(&config.apis.modrinth), &args[0]).await;
31
32 dbg!(&project);
33
34 if project.versions.is_empty() { panic!("This should never happen"); };
35
36 let available_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), current_list.clone()).await;
37
38 let current_version = extract_current_version(available_versions.clone())?;
39
40 //add to current list and mod table
41 match get_mods_from_list(config.clone(), current_list.clone()) {
42 Ok(mods) => {
43 dbg!(&mods);
44 if mods.contains(&project.id) {
45 return Err(Box::new(Error::new(ErrorKind::Other, "MOD_ALREADY_ON_LIST"))); }
46 else {
47 insert_mod_in_list(config.clone(), current_list.clone(), String::from(&project.id), current_version, available_versions)?;
48 }
49 },
50 Err(..) => insert_mod_in_list(config.clone(), current_list, String::from(&project.id), current_version, available_versions)?,
51 };
52
53 match get_mods(config.clone()) {
54 Ok(mods) => {
55 dbg!(&mods);
56 if mods.contains(&project.id) {
57 return Err(Box::new(Error::new(ErrorKind::Other, "MOD_ALREADY_IN_DATABASE")))
58 } else {
59 insert_mod(config.clone(), String::from(&project.id), String::from(&project.title), project.versions)?;
60 }
61 },
62 Err(..) => insert_mod(config.clone(), String::from(&project.id), String::from(&project.title), project.versions)?,
63 };
64
65 Ok(())
66}
67
68fn remove(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
69 if args.is_empty() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))); };
70
71 let current_list = get_current_list(config.clone())?;
72 let mod_id = get_mod_id(config.clone(), String::from(&args[0]))?;
73
74 //TODO implement remove from modlist if not in any other lists && config clean is true
75 match remove_mod_from_list(config, current_list, mod_id) {
76 Err(err) => { Err(Box::new(err)) },
77 Ok(()) => Ok(()),
78 }
79}
80
81fn extract_current_version(versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> {
82 match versions.len() {
83 0 => Err(Box::new(Error::new(ErrorKind::NotFound, "NO_VERSIONS_AVAILABLE"))),
84 //TODO compare publish dates
85 1.. => Ok(versions[0].id.to_string()),
86 _ => panic!("available_versions should never be negative"),
87 }
88}
diff --git a/src/commands/update.rs b/src/commands/update.rs
new file mode 100644
index 0000000..14c37ec
--- /dev/null
+++ b/src/commands/update.rs
@@ -0,0 +1,40 @@
1use std::io::{Error, ErrorKind};
2
3use crate::{config::Cfg, modrinth::projects, get_current_list, db::{get_mods_from_list, get_versions}};
4
5pub async fn update(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
6
7 let current_list = get_current_list(config.clone())?;
8
9 let mods = get_mods_from_list(config.clone(), current_list)?;
10
11 let mut projects = projects(String::from(&config.apis.modrinth), mods.clone()).await;
12
13 let mut versions = get_versions(config, mods)?;
14
15 projects.sort_by_key(|p| p.id.clone());
16
17 versions.sort_by_key(|v| v.mod_id.clone());
18
19 let mut update_stack: Vec<String> = vec![];
20
21 for (index, project) in projects.iter().enumerate() {
22
23 let cmp_version = &versions[index];
24
25 let p_id = &project.id;
26 let v_id = &cmp_version.mod_id;
27
28 if p_id != v_id { return Err(Box::new(Error::new(ErrorKind::Other, "COMPARE_SORTING_ERR"))); };
29 println!("{}:{}", p_id, v_id);
30
31 if project.versions.join("|") != cmp_version.versions {
32 update_stack.push(String::from(&project.id));
33 };
34 };
35
36 //TODO UPDATE
37 dbg!(update_stack);
38
39 Ok(())
40}
diff --git a/src/config.rs b/src/config.rs
index ba1b46a..58d399a 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,9 +1,10 @@
1use config::{Config, File, FileFormat}; 1use config::{Config, File, FileFormat};
2use serde::Deserialize; 2use serde::Deserialize;
3 3
4#[derive(Debug, Clone,Deserialize)] 4#[derive(Debug, Clone, Deserialize)]
5pub struct Cfg { 5pub struct Cfg {
6 pub data: String, 6 pub data: String,
7 pub clean_remove: bool,
7 pub apis: Apis, 8 pub apis: Apis,
8} 9}
9 10
diff --git a/src/db.rs b/src/db.rs
index bbbca87..33d8344 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -1,36 +1,164 @@
1use std::io::ErrorKind; 1use std::io::ErrorKind;
2 2
3use crate::{Modloader, config::Cfg}; 3use crate::{Modloader, config::Cfg, List, modrinth::Version, get_modloader};
4 4
5//TODO use prepared statements 5//TODO use prepared statements
6 6
7pub fn insert_mod(id: String, name: String, current_version: String, old_versions: Vec<String>, mod_loader: Modloader, desired_mc_version: String) -> Result<(), sqlite::Error> { 7//MODS
8pub fn insert_mod(config: Cfg, id: String, name: String, versions: Vec<String>) -> Result<(), sqlite::Error> {
8 9
9 let connection = sqlite::open("./data.db").unwrap(); 10 println!("Inserting into modlist");
10 11
11 let loader = match mod_loader { 12 let data = format!("{}/data.db", config.data);
12 Modloader::Fabric => "fabric", 13 let connection = sqlite::open(data).unwrap();
13 Modloader::Forge => "forge", 14
14 }; 15 let sql = format!("INSERT INTO mods VALUES ('{}', '{}', '{}')", id, name, versions.join("|"));
16
17 connection.execute(sql)
18}
19
20pub fn insert_mod_in_list(config: Cfg, list: List, id: String, current_version: String, applicable_versions: Vec<Version>) -> Result<(), sqlite::Error> {
21
22 println!("Inserting into current list");
23
24 let data = format!("{}/data.db", config.data);
25 let connection = sqlite::open(data).unwrap();
26
27 let mut applicable_versions_vec = vec![];
28
29 for ver in applicable_versions {
30 applicable_versions_vec.push(ver.id);
31 }
32
33 let sql = format!("INSERT INTO {} VALUES ('{}', '{}', '{}')", list.id, id, current_version, applicable_versions_vec.join("|"));
34
35 connection.execute(sql)
36}
37
38pub fn remove_mod_from_list(config: Cfg, list: List, mod_id: String) -> Result<(), sqlite::Error> {
39 let data = format!("{}/data.db", config.data);
40 let connection = sqlite::open(data).unwrap();
41
42 let sql = format!("DELETE FROM {} WHERE mod_id = '{}'", list.id, mod_id);
15 43
16 let sql = format!("INSERT INTO mods VALUES ('{}', '{}', '{}', '{}', '{}', '{}')", id, name, current_version, old_versions.join("|"), loader, desired_mc_version); 44 dbg!(&sql);
17 45
18 connection.execute(sql) 46 connection.execute(sql)
19} 47}
20 48
21//LIST 49pub fn get_mods(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::Error>> {
22pub fn insert_list(config: Cfg, id: String, mc_version: String, mod_loader: Modloader) -> Result<(), sqlite::Error> { 50
23 let data = format!("{}/data.db", config.data); 51 let data = format!("{}/data.db", config.data);
24 let connection = sqlite::open(data).unwrap(); 52 let connection = sqlite::open(data).unwrap();
53
54 let sql = "SELECT id FROM mods";
25 55
26 //Setup list in table 56 let mut mods: Vec<String> = Vec::new();
27 let loader = match mod_loader { 57 //TODO catch sql errors better
28 Modloader::Fabric => "fabric", 58 connection.iterate(sql, |ids| {
29 Modloader::Forge => "forge", 59 if ids.is_empty() { return false; };
60 for &(_column, value) in ids.iter() {
61 mods.push(String::from(value.unwrap()));
62 }
63 true
64 }).unwrap();
65 match mods.is_empty() {
66 true => Err(Box::new(std::io::Error::new(ErrorKind::NotFound, "NO_MODS"))),
67 false => Ok(mods),
68 }
69}
70
71pub fn get_mods_from_list(config: Cfg, list: List) -> Result<Vec<String>, Box<dyn std::error::Error>> {
72 let data = format!("{}/data.db", config.data);
73 let connection = sqlite::open(data).unwrap();
74
75 let sql = format!("SELECT mod_id FROM {}", list.id);
76
77 let mut mods: Vec<String> = Vec::new();
78 //TODO catch sql errors better
79 connection.iterate(sql, |ids| {
80 if ids.is_empty() { return false; };
81 for &(_column, value) in ids.iter() {
82 mods.push(String::from(value.unwrap()));
83 }
84 true
85 }).unwrap();
86 match mods.is_empty() {
87 true => Err(Box::new(std::io::Error::new(ErrorKind::NotFound, "NO_MODS"))),
88 false => Ok(mods),
89 }
90}
91
92pub fn get_mod_id(config: Cfg, name: String) -> Result<String, Box<dyn std::error::Error>> {
93 let data = format!("{}/data.db", config.data);
94 let connection = sqlite::open(data).unwrap();
95
96 let sql = format!("SELECT id FROM mods WHERE name = '{}'", name);
97
98 dbg!(&sql);
99
100 let mut modification = String::new();
101 //TODO catch sql errors better
102 connection.iterate(sql, |id| {
103 if id.is_empty() { return false; };
104 for &(_column, value) in id.iter() {
105 dbg!(&(_column, value));
106 modification = String::from(value.unwrap());
107 }
108 true
109 }).unwrap();
110
111 dbg!(&modification);
112
113 if modification.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::NotFound, "MOD_NOT_IN_DATABASE"))) };
114
115 Ok(modification)
116}
117
118#[derive(Debug, Clone)]
119pub struct DBModlistVersions {
120 pub mod_id: String,
121 pub versions: String,
122}
123
124pub fn get_versions(config: Cfg, mods: Vec<String>) -> Result<Vec<DBModlistVersions>, Box<dyn std::error::Error>> {
125 let data = format!("{}/data.db", config.data);
126 let connection = sqlite::open(data).unwrap();
127
128 let mut wherestr = String::from("WHERE");
129 for (i, id) in mods.iter().enumerate() {
130 let mut or = " OR";
131 if i == mods.len() - 1 { or = "" }
132 println!("Pushing {}({}) | OR: '{}'", id, i, or);
133 wherestr = format!("{} id = '{}'{}", wherestr, id, or);
134 }
135
136 let sql = format!("SELECT id, versions FROM mods {}", wherestr);
137
138 dbg!(&sql);
139
140 let mut versionmaps: Vec<DBModlistVersions> = Vec::new();
141 //TODO catch sql errors better
142 let mut cursor = connection.prepare(sql).unwrap().into_cursor();
143
144 while let Some(Ok(row)) = cursor.next() {
145 println!("{}: {}", row.get::<String, _>(0), row.get::<String, _>(1));
146 versionmaps.push(DBModlistVersions { mod_id: row.get::<String, _>(0), versions: row.get::<String, _>(1) })
30 }; 147 };
31 148
32 let sql_list = format!("INSERT INTO lists VALUES ('{}', '{}', '{}')", id, mc_version, loader); 149 if versionmaps.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::Other, "NO_MODS_ON_LIST"))); };
33 let sql_table = format!("CREATE TABLE '{}' ( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'mod_loader' TEXT )", id); 150
151 Ok(versionmaps)
152}
153
154
155//LIST
156pub fn insert_list(config: Cfg, id: String, mc_version: String, mod_loader: Modloader) -> Result<(), sqlite::Error> {
157 let data = format!("{}/data.db", config.data);
158 let connection = sqlite::open(data).unwrap();
159
160 let sql_list = format!("INSERT INTO lists VALUES ('{}', '{}', '{}')", id, mc_version, mod_loader.stringify());
161 let sql_table = format!("CREATE TABLE '{}' ( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB)", id);
34 let sql = format!("{};{};", sql_list, sql_table); 162 let sql = format!("{};{};", sql_list, sql_table);
35 163
36 connection.execute(sql) 164 connection.execute(sql)
@@ -68,6 +196,27 @@ pub fn get_lists(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::Error>>
68 } 196 }
69} 197}
70 198
199pub fn get_list(config: Cfg, id: String) -> Result<List, Box<dyn std::error::Error>> {
200 let data = format!("{}/data.db", config.data);
201 let connection = sqlite::open(data).unwrap();
202
203 let sql = format!("SELECT mc_version, modloader FROM lists WHERE id = '{}'", id);
204
205 let mut list = vec![];
206 //TODO catch sql errors better
207 connection.iterate(sql, |ids| {
208 if ids.is_empty() { return false; };
209 for &(_column, value) in ids.iter() {
210 list.push(String::from(value.unwrap()));
211 }
212 true
213 }).unwrap();
214
215 if list.len() != 2 { return Err(Box::new(std::io::Error::new(ErrorKind::InvalidData, "LIST_MISSING_DATA"))) };
216
217 Ok(List { id, mc_version: String::from(&list[0]), modloader: get_modloader(String::from(&list[1]))? })
218}
219
71//config 220//config
72pub fn change_list(config: Cfg, id: String) -> Result<(), sqlite::Error> { 221pub fn change_list(config: Cfg, id: String) -> Result<(), sqlite::Error> {
73 let data = format!("{}/data.db", config.data); 222 let data = format!("{}/data.db", config.data);
@@ -78,7 +227,7 @@ pub fn change_list(config: Cfg, id: String) -> Result<(), sqlite::Error> {
78 connection.execute(sql) 227 connection.execute(sql)
79} 228}
80 229
81pub fn get_current_list(config: Cfg) -> Result<String, Box<dyn std::error::Error>> { 230pub fn get_current_list_id(config: Cfg) -> Result<String, Box<dyn std::error::Error>> {
82 let data = format!("{}/data.db", config.data); 231 let data = format!("{}/data.db", config.data);
83 let connection = sqlite::open(data).unwrap(); 232 let connection = sqlite::open(data).unwrap();
84 233
diff --git a/src/input.rs b/src/input.rs
index 061f1fd..0c13e67 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -1,5 +1,5 @@
1use std::io::{stdin, Error, ErrorKind}; 1use std::io::{stdin, Error, ErrorKind};
2use crate::{add, config::Cfg, list}; 2use crate::{config::Cfg, list, modification, update};
3 3
4pub struct Input { 4pub struct Input {
5 pub command: String, 5 pub command: String,
@@ -41,15 +41,15 @@ pub async fn get_input(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
41 let input = Input::from(user_input.trim().to_string())?; 41 let input = Input::from(user_input.trim().to_string())?;
42 42
43 match input.command.as_str() { 43 match input.command.as_str() {
44 "add" => { 44 "mod" => {
45 if input.args == None { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))) }; 45 modification(config, input.args).await
46 if input.args.as_ref().unwrap().len() != 1 { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_MANY_ARGUMENTS"))) };
47 add(config, input.args.unwrap()[0].to_string()).await?;
48 Ok(())
49 }, 46 },
50 "list" => { 47 "list" => {
51 list(config, input.args) 48 list(config, input.args)
52 }, 49 },
50 "update" => {
51 update(config).await
52 },
53 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_COMMAND"))), 53 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_COMMAND"))),
54 } 54 }
55} 55}
diff --git a/src/lib.rs b/src/lib.rs
index 4ad7c39..e059293 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,11 +4,31 @@ pub mod commands;
4pub mod input; 4pub mod input;
5pub mod db; 5pub mod db;
6 6
7use std::io::{Error, ErrorKind};
8
7pub use apis::*; 9pub use apis::*;
8pub use commands::*; 10pub use commands::*;
9 11
10#[derive(Debug)] 12#[derive(Debug, Clone)]
11pub enum Modloader { 13pub enum Modloader {
12 Fabric, 14 Fabric,
13 Forge 15 Forge
14} 16}
17
18impl Modloader {
19 fn stringify(&self) -> String {
20 match self {
21 Modloader::Fabric => String::from("fabric"),
22 Modloader::Forge => String::from("forge"),
23 }
24 }
25
26}
27
28pub fn get_modloader(string: String) -> Result<Modloader, Box<dyn std::error::Error>> {
29 match string.as_str() {
30 "forge" => Ok(Modloader::Forge),
31 "fabric" => Ok(Modloader::Fabric),
32 _ => Err(Box::new(Error::new(ErrorKind::InvalidData, "UNKNOWN_MODLOADER")))
33 }
34}
diff --git a/tests/db/mod.rs b/tests/db/mod.rs
new file mode 100644
index 0000000..b5aed75
--- /dev/null
+++ b/tests/db/mod.rs
@@ -0,0 +1,5 @@
1use std::fs::File;
2
3pub fn setup() {
4 File::create("./setuptests").unwrap();
5}
diff --git a/tests/db_integration.rs b/tests/db_integration.rs
new file mode 100644
index 0000000..82cfe0f
--- /dev/null
+++ b/tests/db_integration.rs
@@ -0,0 +1,7 @@
1mod db;
2
3#[test]
4fn test_add() {
5 db::setup();
6 assert!(true);
7}