summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--config.ini6
-rw-r--r--data.dbbin0 -> 36864 bytes
-rw-r--r--planmodlist.xoppbin132624 -> 130585 bytes
-rw-r--r--src/commands/add.rs28
-rw-r--r--src/commands/list.rs80
-rw-r--r--src/commands/mod.rs2
-rw-r--r--src/config.rs6
-rw-r--r--src/db.rs87
-rw-r--r--src/input.rs12
-rw-r--r--src/lib.rs1
11 files changed, 204 insertions, 20 deletions
diff --git a/.gitignore b/.gitignore
index 29da4f7..ec088fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
1/target 1/target
2/api-tests 2/api-tests
3data.db 3/data
4.planmodlist.autosave.xopp 4.planmodlist.autosave.xopp
diff --git a/config.ini b/config.ini
index 0ea32d5..ce33108 100644
--- a/config.ini
+++ b/config.ini
@@ -1,3 +1,5 @@
1data = "./"
2
1[apis] 3[apis]
2;modrinth = "http://localhost:8080/" 4modrinth = "http://localhost:8080/"
3modrinth = "https://api.modrinth.com/v2/" 5;modrinth = "https://api.modrinth.com/v2/"
diff --git a/data.db b/data.db
new file mode 100644
index 0000000..60431f1
--- /dev/null
+++ b/data.db
Binary files differ
diff --git a/planmodlist.xopp b/planmodlist.xopp
index 60fbb4e..6cf4021 100644
--- a/planmodlist.xopp
+++ b/planmodlist.xopp
Binary files differ
diff --git a/src/commands/add.rs b/src/commands/add.rs
index 67f63de..ed4a6d8 100644
--- a/src/commands/add.rs
+++ b/src/commands/add.rs
@@ -1,11 +1,28 @@
1use std::io::{Error, ErrorKind}; 1use std::io::{Error, ErrorKind};
2 2
3use crate::{modrinth::{project, versions}, config::Cfg, db::insert_mod, Modloader}; 3use crate::{modrinth::{project, versions}, config::Cfg, db::insert_mod, Modloader, input::Input};
4 4
5pub async fn add(config: Cfg, mc_mod: String) -> Result<(), Box<dyn std::error::Error>> { 5pub async fn modification(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> {
6 println!("Adding");
7 6
8 let project = project(String::from(&config.apis.modrinth), &mc_mod).await; 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;
9 26
10 dbg!(&project); 27 dbg!(&project);
11 28
@@ -14,7 +31,8 @@ pub async fn add(config: Cfg, mc_mod: String) -> Result<(), Box<dyn std::error::
14 if project.versions.is_empty() { panic!("This should never happen"); }; 31 if project.versions.is_empty() { panic!("This should never happen"); };
15 32
16 let current_version = get_current(config, String::from(&project.id)).await?; 33 let current_version = get_current(config, String::from(&project.id)).await?;
17 34
35 //add to current list and mod table
18 match insert_mod(project.id, project.title, current_version, project.versions, loader, String::from("1.19.2")) { 36 match insert_mod(project.id, project.title, current_version, project.versions, loader, String::from("1.19.2")) {
19 Err(err) => { Err(Box::new(err)) }, 37 Err(err) => { Err(Box::new(err)) },
20 Ok(()) => Ok(()), 38 Ok(()) => Ok(()),
diff --git a/src/commands/list.rs b/src/commands/list.rs
new file mode 100644
index 0000000..6c260ce
--- /dev/null
+++ b/src/commands/list.rs
@@ -0,0 +1,80 @@
1use std::io::{Error, ErrorKind};
2
3use crate::{db::{insert_list, remove_list, change_list, get_lists, get_current_list}, Modloader, config::Cfg, input::Input};
4
5pub fn list(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> {
6
7 if args.is_none() {
8 let lists = get_lists(config.clone())?;
9 let current_list = get_current_list(config)?;
10 println!("Your lists:\n{}\n-----\nCurrently selected list: \"{}\"", lists.join(",\n"), current_list);
11 return Ok(());
12 }
13
14 let arguments = Input::from(args.unwrap().join(" "))?;
15
16 if arguments.args.is_none() { return Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))); };
17
18 match arguments.command.as_str() {
19 "add" => {
20 add(config, arguments.args.unwrap())
21 },
22 "change" => {
23 change(config, arguments.args.unwrap())
24 },
25 "remove" => {
26 remove(config, arguments.args.unwrap())
27 },
28 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_SUBCOMMAND")))
29 }
30}
31
32fn add(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
33 match args.len() {
34 1 | 2 => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_FEW_ARGUMENTS"))),
35 3 => {
36 let id = String::from(&args[0]);
37 let mc_version = String::from(&args[1]);
38 let mod_loader = match args[2].as_str() {
39 "forge" => Modloader::Forge,
40 "fabric" => Modloader::Fabric,
41 _ => return Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_MODLOADER")))
42 };
43 match insert_list(config, id, mc_version, mod_loader) {
44 Err(err) => { Err(Box::new(err)) },
45 Ok(()) => Ok(()),
46 }
47 },
48 5.. => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_MANY_ARGUMENTS"))),
49 _ => panic!("list arguments should never be zero or lower"),
50 }
51}
52
53fn change(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
54 let lists = get_lists(config.clone())?;
55 match args.len() {
56 1 => {
57 let list = String::from(&args[0]);
58 if !lists.contains(&list) { return Err(Box::new(Error::new(ErrorKind::NotFound, "LIST_DOESNT_EXIST"))); };
59 match change_list(config, list) {
60 Err(err) => { Err(Box::new(err)) },
61 Ok(()) => Ok(()),
62 }
63 },
64 2.. => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_MANY_ARGUMENTS"))),
65 _ => panic!("list arguments should never be zero or lower"),
66 }
67}
68
69fn remove(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
70 match args.len() {
71 1 => {
72 match remove_list(config, String::from(&args[0])) {
73 Err(err) => { Err(Box::new(err)) },
74 Ok(()) => Ok(()),
75 }
76 },
77 2.. => Err(Box::new(Error::new(ErrorKind::InvalidInput, "TOO_MANY_ARGUMENTS"))),
78 _ => panic!("list arguments should never be zero or lower"),
79 }
80}
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 0cc183a..6432746 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -1,3 +1,5 @@
1pub mod add; 1pub mod add;
2pub mod list;
2 3
3pub use add::*; 4pub use add::*;
5pub use list::*;
diff --git a/src/config.rs b/src/config.rs
index a0dfbbe..ba1b46a 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,18 +1,20 @@
1use config::{Config, File, FileFormat}; 1use config::{Config, File, FileFormat};
2use serde::Deserialize; 2use serde::Deserialize;
3 3
4#[derive(Debug, Deserialize)] 4#[derive(Debug, Clone,Deserialize)]
5pub struct Cfg { 5pub struct Cfg {
6 pub data: String,
6 pub apis: Apis, 7 pub apis: Apis,
7} 8}
8 9
9#[derive(Debug, Deserialize)] 10#[derive(Debug, Clone, Deserialize)]
10pub struct Apis { 11pub struct Apis {
11 pub modrinth: String, 12 pub modrinth: String,
12} 13}
13 14
14impl Cfg { 15impl Cfg {
15 pub fn init(path: &str) -> Self { 16 pub fn init(path: &str) -> Self {
17 //TODO Error Handling
16 Config::builder() 18 Config::builder()
17 .add_source(File::new(path, FileFormat::Ini)) 19 .add_source(File::new(path, FileFormat::Ini))
18 .build() 20 .build()
diff --git a/src/db.rs b/src/db.rs
index 3d50b0f..bbbca87 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -1,4 +1,8 @@
1use crate::Modloader; 1use std::io::ErrorKind;
2
3use crate::{Modloader, config::Cfg};
4
5//TODO use prepared statements
2 6
3pub fn insert_mod(id: String, name: String, current_version: String, old_versions: Vec<String>, mod_loader: Modloader, desired_mc_version: String) -> Result<(), sqlite::Error> { 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> {
4 8
@@ -10,9 +14,88 @@ pub fn insert_mod(id: String, name: String, current_version: String, old_version
10 }; 14 };
11 15
12 let sql = format!("INSERT INTO mods VALUES ('{}', '{}', '{}', '{}', '{}', '{}')", id, name, current_version, old_versions.join("|"), loader, desired_mc_version); 16 let sql = format!("INSERT INTO mods VALUES ('{}', '{}', '{}', '{}', '{}', '{}')", id, name, current_version, old_versions.join("|"), loader, desired_mc_version);
17
18 connection.execute(sql)
19}
20
21//LIST
22pub fn insert_list(config: Cfg, id: String, mc_version: String, mod_loader: Modloader) -> Result<(), sqlite::Error> {
23 let data = format!("{}/data.db", config.data);
24 let connection = sqlite::open(data).unwrap();
13 25
14 dbg!(&sql); 26 //Setup list in table
27 let loader = match mod_loader {
28 Modloader::Fabric => "fabric",
29 Modloader::Forge => "forge",
30 };
31
32 let sql_list = format!("INSERT INTO lists VALUES ('{}', '{}', '{}')", id, mc_version, loader);
33 let sql_table = format!("CREATE TABLE '{}' ( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'mod_loader' TEXT )", id);
34 let sql = format!("{};{};", sql_list, sql_table);
15 35
16 connection.execute(sql) 36 connection.execute(sql)
37}
17 38
39pub fn remove_list(config: Cfg, id: String) -> Result<(), sqlite::Error> {
40 let data = format!("{}/data.db", config.data);
41 let connection = sqlite::open(data).unwrap();
42
43 let sql_list = format!("DELETE FROM lists WHERE id = '{}'", id);
44 let sql_table = format!("DROP TABLE '{}'", id);
45 let sql = format!("{};{};", sql_list, sql_table);
46
47 connection.execute(sql)
48}
49
50pub fn get_lists(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::Error>> {
51 let data = format!("{}/data.db", config.data);
52 let connection = sqlite::open(data).unwrap();
53
54 let sql = "SELECT id FROM lists";
55
56 let mut list: Vec<String> = Vec::new();
57 //TODO catch sql errors better
58 connection.iterate(sql, |ids| {
59 if ids.is_empty() { return false; };
60 for &(_column, value) in ids.iter() {
61 list.push(String::from(value.unwrap()));
62 }
63 true
64 }).unwrap();
65 match list.is_empty() {
66 true => Err(Box::new(std::io::Error::new(ErrorKind::NotFound, "NO_LISTS"))),
67 false => Ok(list),
68 }
69}
70
71//config
72pub fn change_list(config: Cfg, id: String) -> Result<(), sqlite::Error> {
73 let data = format!("{}/data.db", config.data);
74 let connection = sqlite::open(data).unwrap();
75
76 let sql = format!("UPDATE user_config SET value = '{}' WHERE id = 'current_list'", id);
77
78 connection.execute(sql)
79}
80
81pub fn get_current_list(config: Cfg) -> Result<String, Box<dyn std::error::Error>> {
82 let data = format!("{}/data.db", config.data);
83 let connection = sqlite::open(data).unwrap();
84
85 let sql = "SELECT id FROM lists";
86
87 let mut list: String = String::new();
88 //TODO catch sql errors better
89 connection.iterate(sql, |ids| {
90 if ids.is_empty() { return false; };
91 for &(_column, value) in ids.iter() {
92 list = String::from(value.unwrap());
93 }
94 true
95 }).unwrap();
96 if list.is_empty() {
97 get_lists(config)?;
98 panic!("current list field should never be empty if there are other lists");
99 };
100 Ok(list)
18} 101}
diff --git a/src/input.rs b/src/input.rs
index 689389e..061f1fd 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}; 2use crate::{add, config::Cfg, list};
3 3
4pub struct Input { 4pub struct Input {
5 pub command: String, 5 pub command: String,
@@ -9,14 +9,9 @@ pub struct Input {
9impl Input { 9impl Input {
10 pub fn from(string: String) -> Result<Self, Box<dyn std::error::Error>> { 10 pub fn from(string: String) -> Result<Self, Box<dyn std::error::Error>> {
11 let mut split: Vec<&str> = string.split(' ').collect(); 11 let mut split: Vec<&str> = string.split(' ').collect();
12
13 let command: String; 12 let command: String;
14 let mut args: Option<Vec<String>> = None; 13 let mut args: Option<Vec<String>> = None;
15
16 if split[0].is_empty() { split.remove(0); }; 14 if split[0].is_empty() { split.remove(0); };
17
18 dbg!(&split);
19
20 match split.len() { 15 match split.len() {
21 0 => { Err(Box::new(Error::new(ErrorKind::InvalidInput, "NO_ARGS"))) } 16 0 => { Err(Box::new(Error::new(ErrorKind::InvalidInput, "NO_ARGS"))) }
22 1 => Ok( Input { command: split[0].to_string(), args }), 17 1 => Ok( Input { command: split[0].to_string(), args }),
@@ -43,8 +38,6 @@ pub async fn get_input(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
43 .read_line(&mut user_input) 38 .read_line(&mut user_input)
44 .expect("ERROR"); 39 .expect("ERROR");
45 40
46 dbg!(&user_input);
47
48 let input = Input::from(user_input.trim().to_string())?; 41 let input = Input::from(user_input.trim().to_string())?;
49 42
50 match input.command.as_str() { 43 match input.command.as_str() {
@@ -54,6 +47,9 @@ pub async fn get_input(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
54 add(config, input.args.unwrap()[0].to_string()).await?; 47 add(config, input.args.unwrap()[0].to_string()).await?;
55 Ok(()) 48 Ok(())
56 }, 49 },
50 "list" => {
51 list(config, input.args)
52 },
57 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_COMMAND"))), 53 _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_COMMAND"))),
58 } 54 }
59} 55}
diff --git a/src/lib.rs b/src/lib.rs
index 52b0646..4ad7c39 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,6 +7,7 @@ pub mod db;
7pub use apis::*; 7pub use apis::*;
8pub use commands::*; 8pub use commands::*;
9 9
10#[derive(Debug)]
10pub enum Modloader { 11pub enum Modloader {
11 Fabric, 12 Fabric,
12 Forge 13 Forge