use std::io::{Error, ErrorKind}; use rusqlite::Connection; use crate::{ config::Cfg, error::{EType, MLErr, MLE}, List, Modloader, }; //MODS /// # Errors pub fn mods_insert(config: &Cfg, id: &str, slug: &str, name: &str) -> MLE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute( "INSERT INTO mods (id, slug, title) VALUES (?1, ?2, ?3)", [id, slug, name.replace('\'', "").as_str()], )?; Ok(()) } /// # Errors pub fn mods_get_all_ids( config: &Cfg, ) -> Result, Box> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut mods: Vec = Vec::new(); let mut stmt = connection.prepare("SELECT id FROM mods")?; let id_iter = stmt.query_map([], |row| row.get::(0))?; for id in id_iter { mods.push(id?); } if mods.is_empty() { Err(Box::new(Error::new(ErrorKind::NotFound, "NO_MODS_ALL"))) } else { Ok(mods) } } /// Get mod id based on the slug or name /// # Arguments /// ///* `data` - file directory of the database ///* `slug` - Slug or Name of a mod /// /// # Errors /// /// Will return `MLError` when no mod id is found pub fn mods_get_id(data: &str, slug: &str) -> MLE { let data = format!("{data}/data.db"); let connection = Connection::open(data)?; let mut mod_id = String::new(); //get from slug let mut stmt = connection.prepare("SELECT id FROM mods WHERE slug = ?")?; let id_iter = stmt.query_map([slug], |row| row.get::(0))?; for id in id_iter { mod_id = id?; } //get from id if no slug found if mod_id.is_empty() { let mut stmt = connection.prepare("SELECT id FROM mods WHERE id = ?")?; let id_iter = stmt.query_map([slug], |row| row.get::(0))?; for id in id_iter { mod_id = id?; } } //get from title if no id found from slug if mod_id.is_empty() { let mut stmt = connection.prepare("SELECT id FROM mods WHERE title = ?")?; let id_iter = stmt.query_map([slug], |row| row.get::(0))?; for id in id_iter { mod_id = id?; } } if mod_id.is_empty() { return Err(MLErr::new(EType::DBError, "GI_MOD_NOT_FOUND")); }; Ok(mod_id) } pub struct ModInfo { pub slug: String, pub title: String, } /// # Errors pub fn mods_get_info(config: &Cfg, id: &str) -> MLE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut mod_info: Option = None; let mut stmt = connection.prepare("SELECT title, slug FROM mods WHERE id = ?")?; let name_iter = stmt.query_map([id], |row| { Ok(vec![ row.get::(0)?, row.get::(1)?, ]) })?; for info in name_iter { let i = info?; mod_info = Some(ModInfo { title: String::from(&i[0]), slug: String::from(&i[1]), }); } if mod_info.is_none() { Err(MLErr::new(EType::DBError, "GN_MOD_NOT_FOUND")) } else { Ok(mod_info.ok_or(MLErr::new(EType::Other, "mod_info"))?) } } /// # Errors pub fn mods_remove(config: &Cfg, id: &str) -> MLE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute("DELETE FROM mods WHERE id = ?", [id])?; Ok(()) } #[derive(Debug, Clone, PartialEq, Eq)] pub struct DBModlistVersions { pub mod_id: String, pub versions: String, } /// # Errors pub fn mods_get_versions( config: &Cfg, mods: &[String], ) -> MLE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; if mods.is_empty() { return Err(MLErr::new(EType::ArgumentError, "MODS_NO_INPUT")); } let mut wherestr = String::from("WHERE"); for (i, id) in mods.iter().enumerate() { let mut or = " OR"; if i == mods.len() - 1 { or = ""; }; wherestr = format!("{wherestr} id = '{id}'{or}"); } let mut versionmaps: Vec = Vec::new(); let mut stmt = connection.prepare( format!("SELECT id, versions, title FROM mods {wherestr}").as_str(), )?; let id_iter = stmt.query_map([], |row| { Ok(vec![ row.get::(0)?, row.get::(1)?, row.get::(2)?, ]) })?; for ver in id_iter { let version = ver?; versionmaps.push(DBModlistVersions { mod_id: String::from(&version[0]), versions: String::from(&version[1]), }); } if versionmaps.is_empty() { Err(MLErr::new(EType::DBError, "MODS_MODS_NOT_FOUND")) } else { Ok(versionmaps) } } //userlist /// # Errors pub fn userlist_insert( config: &Cfg, list_id: &str, mod_id: &str, current_version: &str, applicable_versions: &[String], current_link: &str, set_version: bool, ) -> MLE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let sv = if set_version { "1" } else { "0" }; connection.execute( format!("INSERT INTO {list_id} VALUES (?1, ?2, ?3, ?4, 'NONE', ?5)") .as_str(), [ mod_id, current_version, applicable_versions.join("|").as_str(), current_link, sv, ], )?; Ok(()) } /// # Errors pub fn userlist_get_all_ids(config: &Cfg, list_id: &str) -> MLE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut mod_ids: Vec = Vec::new(); let mut stmt = connection.prepare(format!("SELECT mod_id FROM {list_id}").as_str())?; let id_iter = stmt.query_map([], |row| row.get::(0))?; for id in id_iter { mod_ids.push(id?); } if mod_ids.is_empty() { Err(MLErr::new( EType::DBError, &format!("NO_MODS_USERLIST{list_id}"), )) } else { Ok(mod_ids) } } /// # Errors pub fn userlist_remove(config: &Cfg, list_id: &str, mod_id: &str) -> MLE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute( format!("DELETE FROM {list_id} WHERE mod_id = ?").as_str(), [mod_id], )?; Ok(()) } /// # Errors pub fn userlist_get_applicable_versions( config: &Cfg, list_id: &str, mod_id: String, ) -> MLE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut version: String = String::new(); let mut stmt = connection.prepare( format!("SELECT applicable_versions FROM {list_id} WHERE mod_id = ?") .as_str(), )?; let ver_iter = stmt.query_map([mod_id], |row| row.get::(0))?; for ver in ver_iter { version = ver?; } if version.is_empty() { Err(MLErr::new(EType::DBError, "GAV_MOD_NOT_FOUND")) } else { Ok(version) } } /// # Errors pub fn userlist_get_all_applicable_versions_with_mods( config: &Cfg, list_id: &str, ) -> MLE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut versions: Vec<(String, String)> = Vec::new(); let mut stmt = connection.prepare( format!("SELECT mod_id, applicable_versions FROM {list_id}").as_str(), )?; let id_iter = stmt.query_map([], |row| { Ok(vec![ row.get::(0)?, row.get::(1)?, ]) })?; for ver in id_iter { let out = ver?; versions.push((out[0].clone(), out[1].clone())); } if versions.is_empty() { return Err(MLErr::new(EType::DBError, "NO_MODS_ON_LIST")); }; Ok(versions) } /// # Errors pub fn userlist_get_current_version( config: &Cfg, list_id: &str, mod_id: &str, ) -> MLE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut version: String = String::new(); let mut stmt = connection.prepare( format!("SELECT current_version FROM {list_id} WHERE mod_id = ?") .as_str(), )?; let ver_iter = stmt.query_map([&mod_id], |row| row.get::(0))?; for ver in ver_iter { version = ver?; } if version.is_empty() { Err(MLErr::new(EType::DBError, "GCV_MOD_NOT_FOUND")) } else { Ok(version) } } /// # Errors pub fn userlist_get_all_current_version_ids( config: &Cfg, list_id: &str, ) -> MLE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut versions: Vec = Vec::new(); let mut stmt = connection .prepare(format!("SELECT current_version FROM {list_id}").as_str())?; let id_iter = stmt.query_map([], |row| row.get::(0))?; for id in id_iter { versions.push(id?); } if versions.is_empty() { return Err(MLErr::new(EType::DBError, "NO_MODS_ON_LIST")); }; Ok(versions) } /// # Errors pub fn userlist_get_all_current_versions_with_mods( config: &Cfg, list_id: &str, ) -> Result, Box> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut versions: Vec<(String, String)> = Vec::new(); let mut stmt = connection.prepare( format!("SELECT mod_id, current_version FROM {list_id}").as_str(), )?; let id_iter = stmt.query_map([], |row| { Ok(vec![ row.get::(0)?, row.get::(1)?, ]) })?; for ver in id_iter { let out = ver?; versions.push((out[0].clone(), out[1].clone())); } if versions.is_empty() { return Err(Box::new(std::io::Error::new( ErrorKind::Other, "NO_MODS_ON_LIST", ))); }; Ok(versions) } /// # Errors pub fn userlist_get_set_version( config: &Cfg, list_id: &str, mod_id: &str, ) -> MLE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut set_version: bool = false; let mut stmt = connection.prepare( format!("SELECT set_version FROM {list_id} WHERE mod_id = ?").as_str(), )?; let ver_iter = stmt.query_map([&mod_id], |row| row.get::(0))?; for ver in ver_iter { set_version = ver?; } Ok(set_version) } /// # Errors pub fn userlist_change_versions( config: &Cfg, list_id: &str, current_version: String, versions: String, link: String, mod_id: String, ) -> MLE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute(format!("UPDATE {list_id} SET current_version = ?1, applicable_versions = ?2, current_download = ?3 WHERE mod_id = ?4").as_str(), [current_version, versions, link, mod_id])?; Ok(()) } /// # Errors pub fn userlist_add_disabled_versions( config: &Cfg, list_id: &str, disabled_version: String, mod_id: String, ) -> MLE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let currently_disabled_versions = userlist_get_disabled_versions(config, list_id, String::from(&mod_id))?; let disabled_versions = if currently_disabled_versions == "NONE" { disabled_version } else { format!("{currently_disabled_versions}|{disabled_version}") }; connection.execute( format!( "UPDATE {list_id} SET disabled_versions = ?1 WHERE mod_id = ?2" ) .as_str(), [disabled_versions, mod_id], )?; Ok(()) } /// # Errors pub fn userlist_get_disabled_versions( config: &Cfg, list_id: &str, mod_id: String, ) -> MLE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut version: String = String::new(); let mut stmt = connection.prepare( format!("SELECT disabled_versions FROM {list_id} WHERE mod_id = ?") .as_str(), )?; let ver_iter = stmt.query_map([mod_id], |row| row.get::(0))?; for ver in ver_iter { version = ver?; } if version.is_empty() { Err(MLErr::new(EType::DBError, "GDV_MOD_NOT_FOUND")) } else { Ok(version) } } /// # Errors pub fn userlist_get_all_downloads( config: &Cfg, list_id: &str, ) -> Result, Box> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut links: Vec = Vec::new(); let mut stmt = connection .prepare(format!("SELECT current_download FROM {list_id}").as_str())?; let link_iter = stmt.query_map([], |row| row.get::(0))?; for link in link_iter { let l = link?; links.push(l); } if links.is_empty() { return Err(Box::new(std::io::Error::new( ErrorKind::Other, "NO_MODS_ON_LIST", ))); }; Ok(links) } //lists ///Inserts into lists table and creates new table /// # Errors pub fn lists_insert( config: &Cfg, id: &str, mc_version: &str, mod_loader: &Modloader, download_folder: &str, ) -> MLE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute( "INSERT INTO lists VALUES (?1, ?2, ?3, ?4)", [id, mc_version, &mod_loader.to_string(), download_folder], )?; connection.execute(format!("CREATE TABLE {id}( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'current_download' TEXT, 'disabled_versions' TEXT DEFAULT 'NONE', 'set_version' INTEGER, CONSTRAINT {id}_PK PRIMARY KEY (mod_id) )").as_str(), [])?; Ok(()) } /// # Errors pub fn lists_remove(config: &Cfg, id: &str) -> MLE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute("DELETE FROM lists WHERE id = ?", [&id])?; connection.execute(format!("DROP TABLE {id}").as_str(), [])?; Ok(()) } /// # Errors pub fn lists_get(config: &Cfg, list_id: &str) -> MLE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut list = List { id: String::new(), mc_version: String::new(), modloader: Modloader::Fabric, download_folder: String::new(), }; let mut stmt = connection.prepare( "SELECT mc_version, modloader, download_folder FROM lists WHERE id = ?", )?; let list_iter = stmt.query_map([&list_id], |row| { Ok(vec![ row.get::(0)?, row.get::(1)?, row.get::(2)?, ]) })?; for l in list_iter { let li = l?; list = List { id: list_id.to_string(), mc_version: String::from(&li[0]), modloader: Modloader::from(&li[1])?, download_folder: String::from(&li[2]), }; } if list.id.is_empty() { return Err(MLErr::new(EType::DBError, "LIST_NOT_FOUND")); } Ok(list) } /// # Errors pub fn lists_version(config: &Cfg, list_id: &str, version: &str) -> MLE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute( "UPDATE lists SET mc_version = ? WHERE id = ?", [version, list_id], )?; Ok(()) } /// # Errors pub fn lists_get_all_ids(config: &Cfg) -> MLE> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut list_ids: Vec = Vec::new(); let mut stmt = connection.prepare("SELECT id FROM lists")?; let id_iter = stmt.query_map([], |row| row.get::(0))?; for id in id_iter { list_ids.push(id?); } if list_ids.is_empty() { Err(MLErr::new(EType::DBError, "NO_LISTS")) } else { Ok(list_ids) } } //config /// # Errors pub fn config_change_current_list(config: &Cfg, id: &str) -> MLE<()> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute( "UPDATE user_config SET value = ? WHERE id = 'current_list'", [id], )?; Ok(()) } /// # Errors pub fn config_get_current_list(config: &Cfg) -> MLE { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut list_id = String::new(); let mut stmt = connection .prepare("SELECT value FROM user_config WHERE id = 'current_list'")?; let list_iter = stmt.query_map([], |row| row.get::(0))?; for list in list_iter { list_id = list?; } if list_id.is_empty() { return Err(MLErr::new(EType::DBError, "NO_CURRENT_LIST")); } Ok(list_id) } //SETUP(UPDATES) /// # Errors pub fn s_userlist_update_download( config: &Cfg, list_id: &str, mod_id: String, link: String, ) -> Result<(), Box> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute( format!("UPDATE {list_id} SET current_download = ?1 WHERE mod_id = ?2") .as_str(), [link, mod_id], )?; Ok(()) } /// # Errors pub fn s_config_create_version( config: &Cfg, ) -> Result<(), Box> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute( "INSERT INTO 'user_config' VALUES ( 'db_version', '0.2' )", (), )?; Ok(()) } /// # Errors pub fn s_config_update_version( config: &Cfg, ver: String, ) -> Result<(), Box> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; connection.execute( "UPDATE user_config SET value = ? WHERE id = 'db_version'", [ver], )?; Ok(()) } /// # Errors pub fn s_config_get_version( config: &Cfg, ) -> Result> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut version: String = String::new(); let mut stmt = connection .prepare("SELECT value FROM user_config WHERE id = 'db_version'")?; let ver_iter = stmt.query_map([], |row| row.get::(0))?; for ver in ver_iter { version = ver?; } if version.is_empty() { return Err(Box::new(std::io::Error::new( ErrorKind::Other, "NO_DBVERSION", ))); }; Ok(version) } /// # Errors pub fn s_insert_column( config: &Cfg, table: &str, column: &str, c_type: &str, default: Option, ) -> Result<(), Box> { let data = format!("{}/data.db", config.data); let connection = Connection::open(data)?; let mut sql = format!("ALTER TABLE {table} ADD '{column}' {c_type}"); if default.is_some() { sql = format!( "{} DEFAULT {}", sql, default.ok_or(MLErr::new(EType::Other, "errornous default"))? ); } connection.execute(sql.as_str(), ())?; Ok(()) } /// # Errors pub fn setup(path: &str) -> MLE<()> { let connection = Connection::open(path)?; connection.execute_batch( "CREATE TABLE 'user_config' ( 'id' TEXT, 'value' TEXT ); CREATE TABLE 'mods' ( 'id' TEXT, 'slug' TEXT, 'title' TEXT, CONSTRAINT mods_PK PRIMARY KEY (id) ); CREATE TABLE 'lists' ( 'id' TEXT, 'mc_version' TEXT, 'modloader' TEXT, 'download_folder' TEXT ); INSERT INTO 'user_config' VALUES ( 'db_version', '0.5' ); INSERT INTO 'user_config' VALUES ( 'current_list', '...' )", )?; Ok(()) }