From ebc360ef816b3168dc9293bbfa1ddf2fa4aed72e Mon Sep 17 00:00:00 2001 From: Cameron Garnham Date: Tue, 25 Apr 2023 19:44:25 +0200 Subject: [PATCH] dev: fix clippy warnings for: src/databases/database.rs --- src/app.rs | 4 +- .../commands/import_tracker_statistics.rs | 4 +- src/databases/database.rs | 84 ++++--- src/databases/mysql.rs | 216 +++++++++--------- src/databases/sqlite.rs | 214 ++++++++--------- src/errors.rs | 26 +-- src/tracker/statistics_importer.rs | 4 +- .../databases/sqlite_v1_0_0.rs | 6 +- .../databases/sqlite_v2_0_0.rs | 20 +- tests/databases/mod.rs | 5 +- tests/databases/tests.rs | 7 +- tests/e2e/contexts/torrent/asserts.rs | 4 +- tests/e2e/contexts/user/steps.rs | 4 +- 13 files changed, 301 insertions(+), 297 deletions(-) diff --git a/src/app.rs b/src/app.rs index 3d1f818f..701aa64c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -11,7 +11,7 @@ use crate::bootstrap::logging; use crate::cache::image::manager::ImageCacheService; use crate::common::AppData; use crate::config::Configuration; -use crate::databases::database::connect_database; +use crate::databases::database; use crate::mailer::MailerService; use crate::routes; use crate::tracker::service::Service; @@ -43,7 +43,7 @@ pub async fn run(configuration: Configuration) -> Running { // Build app dependencies - let database = Arc::new(connect_database(&database_connect_url).await.expect("Database error.")); + let database = Arc::new(database::connect(&database_connect_url).await.expect("Database error.")); let auth = Arc::new(AuthorizationService::new(cfg.clone(), database.clone())); let tracker_service = Arc::new(Service::new(cfg.clone(), database.clone()).await); let tracker_statistics_importer = diff --git a/src/console/commands/import_tracker_statistics.rs b/src/console/commands/import_tracker_statistics.rs index 4c12bf66..eb31bb3c 100644 --- a/src/console/commands/import_tracker_statistics.rs +++ b/src/console/commands/import_tracker_statistics.rs @@ -8,7 +8,7 @@ use text_colorizer::Colorize; use crate::bootstrap::config::init_configuration; use crate::bootstrap::logging; -use crate::databases::database::connect_database; +use crate::databases::database; use crate::tracker::service::Service; use crate::tracker::statistics_importer::StatisticsImporter; @@ -77,7 +77,7 @@ pub async fn import(_args: &Arguments) { eprintln!("Tracker url: {}", tracker_url.green()); let database = Arc::new( - connect_database(&settings.database.connect_url) + database::connect(&settings.database.connect_url) .await .expect("Database error."), ); diff --git a/src/databases/database.rs b/src/databases/database.rs index 915b52ce..778c3dfe 100644 --- a/src/databases/database.rs +++ b/src/databases/database.rs @@ -13,7 +13,7 @@ use crate::models::user::{User, UserAuthentication, UserCompact, UserProfile}; /// Database drivers. #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] -pub enum DatabaseDriver { +pub enum Driver { Sqlite3, Mysql, } @@ -50,7 +50,7 @@ pub enum Sorting { /// Database errors. #[derive(Debug)] -pub enum DatabaseError { +pub enum Error { Error, UnrecognizedDatabaseDriver, // when the db path does not start with sqlite or mysql UsernameTaken, @@ -64,7 +64,11 @@ pub enum DatabaseError { } /// Connect to a database. -pub async fn connect_database(db_path: &str) -> Result, DatabaseError> { +/// +/// # Errors +/// +/// This function will return an `Error::UnrecognizedDatabaseDriver` if unable to match database type. +pub async fn connect(db_path: &str) -> Result, Error> { match &db_path.chars().collect::>() as &[char] { ['s', 'q', 'l', 'i', 't', 'e', ..] => { let db = SqliteDatabase::new(db_path).await; @@ -74,7 +78,7 @@ pub async fn connect_database(db_path: &str) -> Result, Databa let db = MysqlDatabase::new(db_path).await; Ok(Box::new(db)) } - _ => Err(DatabaseError::UnrecognizedDatabaseDriver), + _ => Err(Error::UnrecognizedDatabaseDriver), } } @@ -82,58 +86,58 @@ pub async fn connect_database(db_path: &str) -> Result, Databa #[async_trait] pub trait Database: Sync + Send { /// Return current database driver. - fn get_database_driver(&self) -> DatabaseDriver; + fn get_database_driver(&self) -> Driver; /// Add new user and return the newly inserted `user_id`. - async fn insert_user_and_get_id(&self, username: &str, email: &str, password: &str) -> Result; + async fn insert_user_and_get_id(&self, username: &str, email: &str, password: &str) -> Result; /// Get `User` from `user_id`. - async fn get_user_from_id(&self, user_id: i64) -> Result; + async fn get_user_from_id(&self, user_id: i64) -> Result; /// Get `UserAuthentication` from `user_id`. - async fn get_user_authentication_from_id(&self, user_id: i64) -> Result; + async fn get_user_authentication_from_id(&self, user_id: i64) -> Result; /// Get `UserProfile` from `username`. - async fn get_user_profile_from_username(&self, username: &str) -> Result; + async fn get_user_profile_from_username(&self, username: &str) -> Result; /// Get `UserCompact` from `user_id`. - async fn get_user_compact_from_id(&self, user_id: i64) -> Result; + async fn get_user_compact_from_id(&self, user_id: i64) -> Result; /// Get a user's `TrackerKey`. async fn get_user_tracker_key(&self, user_id: i64) -> Option; /// Get total user count. - async fn count_users(&self) -> Result; + async fn count_users(&self) -> Result; /// Ban user with `user_id`, `reason` and `date_expiry`. - async fn ban_user(&self, user_id: i64, reason: &str, date_expiry: NaiveDateTime) -> Result<(), DatabaseError>; + async fn ban_user(&self, user_id: i64, reason: &str, date_expiry: NaiveDateTime) -> Result<(), Error>; /// Grant a user the administrator role. - async fn grant_admin_role(&self, user_id: i64) -> Result<(), DatabaseError>; + async fn grant_admin_role(&self, user_id: i64) -> Result<(), Error>; /// Verify a user's email with `user_id`. - async fn verify_email(&self, user_id: i64) -> Result<(), DatabaseError>; + async fn verify_email(&self, user_id: i64) -> Result<(), Error>; /// Link a `TrackerKey` to a certain user with `user_id`. - async fn add_tracker_key(&self, user_id: i64, tracker_key: &TrackerKey) -> Result<(), DatabaseError>; + async fn add_tracker_key(&self, user_id: i64, tracker_key: &TrackerKey) -> Result<(), Error>; /// Delete user and all related user data with `user_id`. - async fn delete_user(&self, user_id: i64) -> Result<(), DatabaseError>; + async fn delete_user(&self, user_id: i64) -> Result<(), Error>; /// Add a new category and return `category_id`. - async fn insert_category_and_get_id(&self, category_name: &str) -> Result; + async fn insert_category_and_get_id(&self, category_name: &str) -> Result; /// Get `Category` from `category_id`. - async fn get_category_from_id(&self, category_id: i64) -> Result; + async fn get_category_from_id(&self, category_id: i64) -> Result; /// Get `Category` from `category_name`. - async fn get_category_from_name(&self, category_name: &str) -> Result; + async fn get_category_from_name(&self, category_name: &str) -> Result; /// Get all categories as `Vec`. - async fn get_categories(&self) -> Result, DatabaseError>; + async fn get_categories(&self) -> Result, Error>; /// Delete category with `category_name`. - async fn delete_category(&self, category_name: &str) -> Result<(), DatabaseError>; + async fn delete_category(&self, category_name: &str) -> Result<(), Error>; /// Get results of a torrent search in a paginated and sorted form as `TorrentsResponse` from `search`, `categories`, `sort`, `offset` and `page_size`. async fn get_torrents_search_sorted_paginated( @@ -143,7 +147,7 @@ pub trait Database: Sync + Send { sort: &Sorting, offset: u64, page_size: u8, - ) -> Result; + ) -> Result; /// Add new torrent and return the newly inserted `torrent_id` with `torrent`, `uploader_id`, `category_id`, `title` and `description`. async fn insert_torrent_and_get_id( @@ -153,10 +157,10 @@ pub trait Database: Sync + Send { category_id: i64, title: &str, description: &str, - ) -> Result; + ) -> Result; /// Get `Torrent` from `InfoHash`. - async fn get_torrent_from_infohash(&self, infohash: &InfoHash) -> Result { + async fn get_torrent_from_infohash(&self, infohash: &InfoHash) -> Result { let torrent_info = self.get_torrent_info_from_infohash(infohash).await?; let torrent_files = self.get_torrent_files_from_id(torrent_info.torrent_id).await?; @@ -171,7 +175,7 @@ pub trait Database: Sync + Send { } /// Get `Torrent` from `torrent_id`. - async fn get_torrent_from_id(&self, torrent_id: i64) -> Result { + async fn get_torrent_from_id(&self, torrent_id: i64) -> Result { let torrent_info = self.get_torrent_info_from_id(torrent_id).await?; let torrent_files = self.get_torrent_files_from_id(torrent_id).await?; @@ -186,44 +190,38 @@ pub trait Database: Sync + Send { } /// Get torrent's info as `DbTorrentInfo` from `torrent_id`. - async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result; + async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result; /// Get torrent's info as `DbTorrentInfo` from torrent `InfoHash`. - async fn get_torrent_info_from_infohash(&self, info_hash: &InfoHash) -> Result; + async fn get_torrent_info_from_infohash(&self, info_hash: &InfoHash) -> Result; /// Get all torrent's files as `Vec` from `torrent_id`. - async fn get_torrent_files_from_id(&self, torrent_id: i64) -> Result, DatabaseError>; + async fn get_torrent_files_from_id(&self, torrent_id: i64) -> Result, Error>; /// Get all torrent's announce urls as `Vec>` from `torrent_id`. - async fn get_torrent_announce_urls_from_id(&self, torrent_id: i64) -> Result>, DatabaseError>; + async fn get_torrent_announce_urls_from_id(&self, torrent_id: i64) -> Result>, Error>; /// Get `TorrentListing` from `torrent_id`. - async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result; + async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result; /// Get `TorrentListing` from `InfoHash`. - async fn get_torrent_listing_from_infohash(&self, infohash: &InfoHash) -> Result; + async fn get_torrent_listing_from_infohash(&self, infohash: &InfoHash) -> Result; /// Get all torrents as `Vec`. - async fn get_all_torrents_compact(&self) -> Result, DatabaseError>; + async fn get_all_torrents_compact(&self) -> Result, Error>; /// Update a torrent's title with `torrent_id` and `title`. - async fn update_torrent_title(&self, torrent_id: i64, title: &str) -> Result<(), DatabaseError>; + async fn update_torrent_title(&self, torrent_id: i64, title: &str) -> Result<(), Error>; /// Update a torrent's description with `torrent_id` and `description`. - async fn update_torrent_description(&self, torrent_id: i64, description: &str) -> Result<(), DatabaseError>; + async fn update_torrent_description(&self, torrent_id: i64, description: &str) -> Result<(), Error>; /// Update the seeders and leechers info for a torrent with `torrent_id`, `tracker_url`, `seeders` and `leechers`. - async fn update_tracker_info( - &self, - torrent_id: i64, - tracker_url: &str, - seeders: i64, - leechers: i64, - ) -> Result<(), DatabaseError>; + async fn update_tracker_info(&self, torrent_id: i64, tracker_url: &str, seeders: i64, leechers: i64) -> Result<(), Error>; /// Delete a torrent with `torrent_id`. - async fn delete_torrent(&self, torrent_id: i64) -> Result<(), DatabaseError>; + async fn delete_torrent(&self, torrent_id: i64) -> Result<(), Error>; /// DELETES ALL DATABASE ROWS, ONLY CALL THIS IF YOU KNOW WHAT YOU'RE DOING! - async fn delete_all_database_rows(&self) -> Result<(), DatabaseError>; + async fn delete_all_database_rows(&self) -> Result<(), Error>; } diff --git a/src/databases/mysql.rs b/src/databases/mysql.rs index 38e07e8b..d4bf4789 100644 --- a/src/databases/mysql.rs +++ b/src/databases/mysql.rs @@ -3,7 +3,8 @@ use chrono::NaiveDateTime; use sqlx::mysql::MySqlPoolOptions; use sqlx::{query, query_as, Acquire, MySqlPool}; -use crate::databases::database::{Category, Database, DatabaseDriver, DatabaseError, Sorting, TorrentCompact}; +use crate::databases::database; +use crate::databases::database::{Category, Database, Driver, Sorting, TorrentCompact}; use crate::models::info_hash::InfoHash; use crate::models::response::TorrentsResponse; use crate::models::torrent::TorrentListing; @@ -35,23 +36,23 @@ impl MysqlDatabase { #[async_trait] impl Database for MysqlDatabase { - fn get_database_driver(&self) -> DatabaseDriver { - DatabaseDriver::Mysql + fn get_database_driver(&self) -> Driver { + Driver::Mysql } - async fn insert_user_and_get_id(&self, username: &str, email: &str, password_hash: &str) -> Result { + async fn insert_user_and_get_id(&self, username: &str, email: &str, password_hash: &str) -> Result { // open pool connection - let mut conn = self.pool.acquire().await.map_err(|_| DatabaseError::Error)?; + let mut conn = self.pool.acquire().await.map_err(|_| database::Error::Error)?; // start db transaction - let mut tx = conn.begin().await.map_err(|_| DatabaseError::Error)?; + let mut tx = conn.begin().await.map_err(|_| database::Error::Error)?; // create the user account and get the user id let user_id = query("INSERT INTO torrust_users (date_registered) VALUES (UTC_TIMESTAMP())") .execute(&mut tx) .await .map(|v| v.last_insert_id()) - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; // add password hash for account let insert_user_auth_result = query("INSERT INTO torrust_user_authentication (user_id, password_hash) VALUES (?, ?)") @@ -59,7 +60,7 @@ impl Database for MysqlDatabase { .bind(password_hash) .execute(&mut tx) .await - .map_err(|_| DatabaseError::Error); + .map_err(|_| database::Error::Error); // rollback transaction on error if let Err(e) = insert_user_auth_result { @@ -77,14 +78,14 @@ impl Database for MysqlDatabase { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("username") { - DatabaseError::UsernameTaken + database::Error::UsernameTaken } else if err.message().contains("email") { - DatabaseError::EmailTaken + database::Error::EmailTaken } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error + _ => database::Error::Error }); // commit or rollback transaction and return user_id on success @@ -100,36 +101,36 @@ impl Database for MysqlDatabase { } } - async fn get_user_from_id(&self, user_id: i64) -> Result { + async fn get_user_from_id(&self, user_id: i64) -> Result { query_as::<_, User>("SELECT * FROM torrust_users WHERE user_id = ?") .bind(user_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::UserNotFound) + .map_err(|_| database::Error::UserNotFound) } - async fn get_user_authentication_from_id(&self, user_id: i64) -> Result { + async fn get_user_authentication_from_id(&self, user_id: i64) -> Result { query_as::<_, UserAuthentication>("SELECT * FROM torrust_user_authentication WHERE user_id = ?") .bind(user_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::UserNotFound) + .map_err(|_| database::Error::UserNotFound) } - async fn get_user_profile_from_username(&self, username: &str) -> Result { + async fn get_user_profile_from_username(&self, username: &str) -> Result { query_as::<_, UserProfile>(r#"SELECT user_id, username, COALESCE(email, "") as email, email_verified, COALESCE(bio, "") as bio, COALESCE(avatar, "") as avatar FROM torrust_user_profiles WHERE username = ?"#) .bind(username) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::UserNotFound) + .map_err(|_| database::Error::UserNotFound) } - async fn get_user_compact_from_id(&self, user_id: i64) -> Result { + async fn get_user_compact_from_id(&self, user_id: i64) -> Result { query_as::<_, UserCompact>("SELECT tu.user_id, tp.username, tu.administrator FROM torrust_users tu INNER JOIN torrust_user_profiles tp ON tu.user_id = tp.user_id WHERE tu.user_id = ?") .bind(user_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::UserNotFound) + .map_err(|_| database::Error::UserNotFound) } async fn get_user_tracker_key(&self, user_id: i64) -> Option { @@ -147,15 +148,15 @@ impl Database for MysqlDatabase { .ok() } - async fn count_users(&self) -> Result { + async fn count_users(&self) -> Result { query_as("SELECT COUNT(*) FROM torrust_users") .fetch_one(&self.pool) .await .map(|(v,)| v) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn ban_user(&self, user_id: i64, reason: &str, date_expiry: NaiveDateTime) -> Result<(), DatabaseError> { + async fn ban_user(&self, user_id: i64, reason: &str, date_expiry: NaiveDateTime) -> Result<(), database::Error> { // date needs to be in ISO 8601 format let date_expiry_string = date_expiry.format("%Y-%m-%d %H:%M:%S").to_string(); @@ -166,40 +167,40 @@ impl Database for MysqlDatabase { .execute(&self.pool) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn grant_admin_role(&self, user_id: i64) -> Result<(), DatabaseError> { + async fn grant_admin_role(&self, user_id: i64) -> Result<(), database::Error> { query("UPDATE torrust_users SET administrator = TRUE WHERE user_id = ?") .bind(user_id) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::UserNotFound) + Err(database::Error::UserNotFound) } }) } - async fn verify_email(&self, user_id: i64) -> Result<(), DatabaseError> { + async fn verify_email(&self, user_id: i64) -> Result<(), database::Error> { query("UPDATE torrust_user_profiles SET email_verified = TRUE WHERE user_id = ?") .bind(user_id) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::UserNotFound) + Err(database::Error::UserNotFound) } }) } - async fn add_tracker_key(&self, user_id: i64, tracker_key: &TrackerKey) -> Result<(), DatabaseError> { + async fn add_tracker_key(&self, user_id: i64, tracker_key: &TrackerKey) -> Result<(), database::Error> { let key = tracker_key.key.clone(); query("INSERT INTO torrust_tracker_keys (user_id, tracker_key, date_expiry) VALUES (?, ?, ?)") @@ -209,25 +210,25 @@ impl Database for MysqlDatabase { .execute(&self.pool) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn delete_user(&self, user_id: i64) -> Result<(), DatabaseError> { + async fn delete_user(&self, user_id: i64) -> Result<(), database::Error> { query("DELETE FROM torrust_users WHERE user_id = ?") .bind(user_id) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::UserNotFound) + Err(database::Error::UserNotFound) } }) } - async fn insert_category_and_get_id(&self, category_name: &str) -> Result { + async fn insert_category_and_get_id(&self, category_name: &str) -> Result { query("INSERT INTO torrust_categories (name) VALUES (?)") .bind(category_name) .execute(&self.pool) @@ -236,49 +237,49 @@ impl Database for MysqlDatabase { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("UNIQUE") { - DatabaseError::CategoryAlreadyExists + database::Error::CategoryAlreadyExists } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error, + _ => database::Error::Error, }) } - async fn get_category_from_id(&self, category_id: i64) -> Result { + async fn get_category_from_id(&self, category_id: i64) -> Result { query_as::<_, Category>("SELECT category_id, name, (SELECT COUNT(*) FROM torrust_torrents WHERE torrust_torrents.category_id = torrust_categories.category_id) AS num_torrents FROM torrust_categories WHERE category_id = ?") .bind(category_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::CategoryNotFound) + .map_err(|_| database::Error::CategoryNotFound) } - async fn get_category_from_name(&self, category_name: &str) -> Result { + async fn get_category_from_name(&self, category_name: &str) -> Result { query_as::<_, Category>("SELECT category_id, name, (SELECT COUNT(*) FROM torrust_torrents WHERE torrust_torrents.category_id = torrust_categories.category_id) AS num_torrents FROM torrust_categories WHERE name = ?") .bind(category_name) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::CategoryNotFound) + .map_err(|_| database::Error::CategoryNotFound) } - async fn get_categories(&self) -> Result, DatabaseError> { + async fn get_categories(&self) -> Result, database::Error> { query_as::<_, Category>("SELECT tc.category_id, tc.name, COUNT(tt.category_id) as num_torrents FROM torrust_categories tc LEFT JOIN torrust_torrents tt on tc.category_id = tt.category_id GROUP BY tc.name") .fetch_all(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn delete_category(&self, category_name: &str) -> Result<(), DatabaseError> { + async fn delete_category(&self, category_name: &str) -> Result<(), database::Error> { query("DELETE FROM torrust_categories WHERE name = ?") .bind(category_name) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::CategoryNotFound) + Err(database::Error::CategoryNotFound) } }) } @@ -291,7 +292,7 @@ impl Database for MysqlDatabase { sort: &Sorting, offset: u64, limit: u8, - ) -> Result { + ) -> Result { let title = match search { None => "%".to_string(), Some(v) => format!("%{}%", v), @@ -351,12 +352,12 @@ impl Database for MysqlDatabase { let count_query = format!("SELECT COUNT(*) as count FROM ({}) AS count_table", query_string); - let count_result: Result = query_as(&count_query) + let count_result: Result = query_as(&count_query) .bind(title.clone()) .fetch_one(&self.pool) .await .map(|(v,)| v) - .map_err(|_| DatabaseError::Error); + .map_err(|_| database::Error::Error); let count = count_result?; @@ -368,7 +369,7 @@ impl Database for MysqlDatabase { .bind(limit) .fetch_all(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; Ok(TorrentsResponse { total: count as u32, @@ -383,20 +384,20 @@ impl Database for MysqlDatabase { category_id: i64, title: &str, description: &str, - ) -> Result { + ) -> Result { let info_hash = torrent.info_hash(); // open pool connection - let mut conn = self.pool.acquire().await.map_err(|_| DatabaseError::Error)?; + let mut conn = self.pool.acquire().await.map_err(|_| database::Error::Error)?; // start db transaction - let mut tx = conn.begin().await.map_err(|_| DatabaseError::Error)?; + let mut tx = conn.begin().await.map_err(|_| database::Error::Error)?; // torrent file can only hold a pieces key or a root hash key: http://www.bittorrent.org/beps/bep_0030.html let (pieces, root_hash): (String, bool) = if let Some(pieces) = &torrent.info.pieces { (bytes_to_hex(pieces.as_ref()), false) } else { - let root_hash = torrent.info.root_hash.as_ref().ok_or(DatabaseError::Error)?; + let root_hash = torrent.info.root_hash.as_ref().ok_or(database::Error::Error)?; (root_hash.to_string(), true) }; @@ -419,14 +420,14 @@ impl Database for MysqlDatabase { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("info_hash") { - DatabaseError::TorrentAlreadyExists + database::Error::TorrentAlreadyExists } else if err.message().contains("title") { - DatabaseError::TorrentTitleAlreadyExists + database::Error::TorrentTitleAlreadyExists } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error + _ => database::Error::Error })?; let insert_torrent_files_result = if let Some(length) = torrent.info.length { @@ -437,7 +438,7 @@ impl Database for MysqlDatabase { .execute(&mut tx) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } else { let files = torrent.info.files.as_ref().unwrap(); @@ -451,7 +452,7 @@ impl Database for MysqlDatabase { .bind(path) .execute(&mut tx) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; } Ok(()) @@ -463,7 +464,8 @@ impl Database for MysqlDatabase { return Err(e); } - let insert_torrent_announce_urls_result: Result<(), DatabaseError> = if let Some(announce_urls) = &torrent.announce_list { + let insert_torrent_announce_urls_result: Result<(), database::Error> = if let Some(announce_urls) = &torrent.announce_list + { // flatten the nested vec (this will however remove the) let announce_urls = announce_urls.iter().flatten().collect::>(); @@ -474,7 +476,7 @@ impl Database for MysqlDatabase { .execute(&mut tx) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; } Ok(()) @@ -487,7 +489,7 @@ impl Database for MysqlDatabase { .execute(&mut tx) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) }; // rollback transaction on error @@ -506,14 +508,14 @@ impl Database for MysqlDatabase { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("info_hash") { - DatabaseError::TorrentAlreadyExists + database::Error::TorrentAlreadyExists } else if err.message().contains("title") { - DatabaseError::TorrentTitleAlreadyExists + database::Error::TorrentTitleAlreadyExists } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error, + _ => database::Error::Error, }); // commit or rollback transaction and return user_id on success @@ -529,33 +531,33 @@ impl Database for MysqlDatabase { } } - async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result { + async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result { query_as::<_, DbTorrentInfo>( "SELECT torrent_id, info_hash, name, pieces, piece_length, private, root_hash FROM torrust_torrents WHERE torrent_id = ?", ) .bind(torrent_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn get_torrent_info_from_infohash(&self, infohash: &InfoHash) -> Result { + async fn get_torrent_info_from_infohash(&self, infohash: &InfoHash) -> Result { query_as::<_, DbTorrentInfo>( "SELECT torrent_id, info_hash, name, pieces, piece_length, private, root_hash FROM torrust_torrents WHERE info_hash = ?", ) .bind(infohash.to_hex_string().to_uppercase()) // `info_hash` is stored as uppercase hex string .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn get_torrent_files_from_id(&self, torrent_id: i64) -> Result, DatabaseError> { + async fn get_torrent_files_from_id(&self, torrent_id: i64) -> Result, database::Error> { let db_torrent_files = query_as::<_, DbTorrentFile>("SELECT md5sum, length, path FROM torrust_torrent_files WHERE torrent_id = ?") .bind(torrent_id) .fetch_all(&self.pool) .await - .map_err(|_| DatabaseError::TorrentNotFound)?; + .map_err(|_| database::Error::TorrentNotFound)?; let torrent_files: Vec = db_torrent_files .into_iter() @@ -574,16 +576,16 @@ impl Database for MysqlDatabase { Ok(torrent_files) } - async fn get_torrent_announce_urls_from_id(&self, torrent_id: i64) -> Result>, DatabaseError> { + async fn get_torrent_announce_urls_from_id(&self, torrent_id: i64) -> Result>, database::Error> { query_as::<_, DbTorrentAnnounceUrl>("SELECT tracker_url FROM torrust_torrent_announce_urls WHERE torrent_id = ?") .bind(torrent_id) .fetch_all(&self.pool) .await .map(|v| v.iter().map(|a| vec![a.tracker_url.to_string()]).collect()) - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result { + async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result { query_as::<_, TorrentListing>( "SELECT tt.torrent_id, tp.username AS uploader, tt.info_hash, ti.title, ti.description, tt.category_id, DATE_FORMAT(tt.date_uploaded, '%Y-%m-%d %H:%i:%s') AS date_uploaded, tt.size AS file_size, CAST(COALESCE(sum(ts.seeders),0) as signed) as seeders, @@ -598,10 +600,10 @@ impl Database for MysqlDatabase { .bind(torrent_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn get_torrent_listing_from_infohash(&self, infohash: &InfoHash) -> Result { + async fn get_torrent_listing_from_infohash(&self, infohash: &InfoHash) -> Result { query_as::<_, TorrentListing>( "SELECT tt.torrent_id, tp.username AS uploader, tt.info_hash, ti.title, ti.description, tt.category_id, DATE_FORMAT(tt.date_uploaded, '%Y-%m-%d %H:%i:%s') AS date_uploaded, tt.size AS file_size, CAST(COALESCE(sum(ts.seeders),0) as signed) as seeders, @@ -616,17 +618,17 @@ impl Database for MysqlDatabase { .bind(infohash.to_hex_string().to_uppercase()) // `info_hash` is stored as uppercase hex string .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn get_all_torrents_compact(&self) -> Result, DatabaseError> { + async fn get_all_torrents_compact(&self) -> Result, database::Error> { query_as::<_, TorrentCompact>("SELECT torrent_id, info_hash FROM torrust_torrents") .fetch_all(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn update_torrent_title(&self, torrent_id: i64, title: &str) -> Result<(), DatabaseError> { + async fn update_torrent_title(&self, torrent_id: i64, title: &str) -> Result<(), database::Error> { query("UPDATE torrust_torrent_info SET title = ? WHERE torrent_id = ?") .bind(title) .bind(torrent_id) @@ -635,34 +637,34 @@ impl Database for MysqlDatabase { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("UNIQUE") { - DatabaseError::TorrentTitleAlreadyExists + database::Error::TorrentTitleAlreadyExists } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error, + _ => database::Error::Error, }) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::TorrentNotFound) + Err(database::Error::TorrentNotFound) } }) } - async fn update_torrent_description(&self, torrent_id: i64, description: &str) -> Result<(), DatabaseError> { + async fn update_torrent_description(&self, torrent_id: i64, description: &str) -> Result<(), database::Error> { query("UPDATE torrust_torrent_info SET description = ? WHERE torrent_id = ?") .bind(description) .bind(torrent_id) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::TorrentNotFound) + Err(database::Error::TorrentNotFound) } }) } @@ -673,7 +675,7 @@ impl Database for MysqlDatabase { tracker_url: &str, seeders: i64, leechers: i64, - ) -> Result<(), DatabaseError> { + ) -> Result<(), database::Error> { query("REPLACE INTO torrust_torrent_tracker_stats (torrent_id, tracker_url, seeders, leechers) VALUES (?, ?, ?, ?)") .bind(torrent_id) .bind(tracker_url) @@ -682,74 +684,74 @@ impl Database for MysqlDatabase { .execute(&self.pool) .await .map(|_| ()) - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn delete_torrent(&self, torrent_id: i64) -> Result<(), DatabaseError> { + async fn delete_torrent(&self, torrent_id: i64) -> Result<(), database::Error> { query("DELETE FROM torrust_torrents WHERE torrent_id = ?") .bind(torrent_id) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::TorrentNotFound) + Err(database::Error::TorrentNotFound) } }) } - async fn delete_all_database_rows(&self) -> Result<(), DatabaseError> { + async fn delete_all_database_rows(&self) -> Result<(), database::Error> { query("DELETE FROM torrust_categories;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_torrents;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_tracker_keys;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_users;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_user_authentication;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_user_bans;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_user_invitations;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_user_profiles;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_torrents;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_user_public_keys;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; Ok(()) } diff --git a/src/databases/sqlite.rs b/src/databases/sqlite.rs index 936dd8d5..6cbc02a2 100644 --- a/src/databases/sqlite.rs +++ b/src/databases/sqlite.rs @@ -3,7 +3,8 @@ use chrono::NaiveDateTime; use sqlx::sqlite::SqlitePoolOptions; use sqlx::{query, query_as, Acquire, SqlitePool}; -use crate::databases::database::{Category, Database, DatabaseDriver, DatabaseError, Sorting, TorrentCompact}; +use crate::databases::database; +use crate::databases::database::{Category, Database, Driver, Sorting, TorrentCompact}; use crate::models::info_hash::InfoHash; use crate::models::response::TorrentsResponse; use crate::models::torrent::TorrentListing; @@ -35,16 +36,16 @@ impl SqliteDatabase { #[async_trait] impl Database for SqliteDatabase { - fn get_database_driver(&self) -> DatabaseDriver { - DatabaseDriver::Sqlite3 + fn get_database_driver(&self) -> Driver { + Driver::Sqlite3 } - async fn insert_user_and_get_id(&self, username: &str, email: &str, password_hash: &str) -> Result { + async fn insert_user_and_get_id(&self, username: &str, email: &str, password_hash: &str) -> Result { // open pool connection - let mut conn = self.pool.acquire().await.map_err(|_| DatabaseError::Error)?; + let mut conn = self.pool.acquire().await.map_err(|_| database::Error::Error)?; // start db transaction - let mut tx = conn.begin().await.map_err(|_| DatabaseError::Error)?; + let mut tx = conn.begin().await.map_err(|_| database::Error::Error)?; // create the user account and get the user id let user_id = @@ -52,7 +53,7 @@ impl Database for SqliteDatabase { .execute(&mut tx) .await .map(|v| v.last_insert_rowid()) - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; // add password hash for account let insert_user_auth_result = query("INSERT INTO torrust_user_authentication (user_id, password_hash) VALUES (?, ?)") @@ -60,7 +61,7 @@ impl Database for SqliteDatabase { .bind(password_hash) .execute(&mut tx) .await - .map_err(|_| DatabaseError::Error); + .map_err(|_| database::Error::Error); // rollback transaction on error if let Err(e) = insert_user_auth_result { @@ -78,14 +79,14 @@ impl Database for SqliteDatabase { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("username") { - DatabaseError::UsernameTaken + database::Error::UsernameTaken } else if err.message().contains("email") { - DatabaseError::EmailTaken + database::Error::EmailTaken } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error + _ => database::Error::Error }); // commit or rollback transaction and return user_id on success @@ -101,36 +102,36 @@ impl Database for SqliteDatabase { } } - async fn get_user_from_id(&self, user_id: i64) -> Result { + async fn get_user_from_id(&self, user_id: i64) -> Result { query_as::<_, User>("SELECT * FROM torrust_users WHERE user_id = ?") .bind(user_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::UserNotFound) + .map_err(|_| database::Error::UserNotFound) } - async fn get_user_authentication_from_id(&self, user_id: i64) -> Result { + async fn get_user_authentication_from_id(&self, user_id: i64) -> Result { query_as::<_, UserAuthentication>("SELECT * FROM torrust_user_authentication WHERE user_id = ?") .bind(user_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::UserNotFound) + .map_err(|_| database::Error::UserNotFound) } - async fn get_user_profile_from_username(&self, username: &str) -> Result { + async fn get_user_profile_from_username(&self, username: &str) -> Result { query_as::<_, UserProfile>("SELECT * FROM torrust_user_profiles WHERE username = ?") .bind(username) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::UserNotFound) + .map_err(|_| database::Error::UserNotFound) } - async fn get_user_compact_from_id(&self, user_id: i64) -> Result { + async fn get_user_compact_from_id(&self, user_id: i64) -> Result { query_as::<_, UserCompact>("SELECT tu.user_id, tp.username, tu.administrator FROM torrust_users tu INNER JOIN torrust_user_profiles tp ON tu.user_id = tp.user_id WHERE tu.user_id = ?") .bind(user_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::UserNotFound) + .map_err(|_| database::Error::UserNotFound) } async fn get_user_tracker_key(&self, user_id: i64) -> Option { @@ -148,15 +149,15 @@ impl Database for SqliteDatabase { .ok() } - async fn count_users(&self) -> Result { + async fn count_users(&self) -> Result { query_as("SELECT COUNT(*) FROM torrust_users") .fetch_one(&self.pool) .await .map(|(v,)| v) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn ban_user(&self, user_id: i64, reason: &str, date_expiry: NaiveDateTime) -> Result<(), DatabaseError> { + async fn ban_user(&self, user_id: i64, reason: &str, date_expiry: NaiveDateTime) -> Result<(), database::Error> { // date needs to be in ISO 8601 format let date_expiry_string = date_expiry.format("%Y-%m-%d %H:%M:%S").to_string(); @@ -167,34 +168,34 @@ impl Database for SqliteDatabase { .execute(&self.pool) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn grant_admin_role(&self, user_id: i64) -> Result<(), DatabaseError> { + async fn grant_admin_role(&self, user_id: i64) -> Result<(), database::Error> { query("UPDATE torrust_users SET administrator = TRUE WHERE user_id = ?") .bind(user_id) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::UserNotFound) + Err(database::Error::UserNotFound) } }) } - async fn verify_email(&self, user_id: i64) -> Result<(), DatabaseError> { + async fn verify_email(&self, user_id: i64) -> Result<(), database::Error> { query("UPDATE torrust_user_profiles SET email_verified = TRUE WHERE user_id = ?") .bind(user_id) .execute(&self.pool) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn add_tracker_key(&self, user_id: i64, tracker_key: &TrackerKey) -> Result<(), DatabaseError> { + async fn add_tracker_key(&self, user_id: i64, tracker_key: &TrackerKey) -> Result<(), database::Error> { let key = tracker_key.key.clone(); query("INSERT INTO torrust_tracker_keys (user_id, tracker_key, date_expiry) VALUES ($1, $2, $3)") @@ -204,25 +205,25 @@ impl Database for SqliteDatabase { .execute(&self.pool) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn delete_user(&self, user_id: i64) -> Result<(), DatabaseError> { + async fn delete_user(&self, user_id: i64) -> Result<(), database::Error> { query("DELETE FROM torrust_users WHERE user_id = ?") .bind(user_id) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::UserNotFound) + Err(database::Error::UserNotFound) } }) } - async fn insert_category_and_get_id(&self, category_name: &str) -> Result { + async fn insert_category_and_get_id(&self, category_name: &str) -> Result { query("INSERT INTO torrust_categories (name) VALUES (?)") .bind(category_name) .execute(&self.pool) @@ -231,49 +232,49 @@ impl Database for SqliteDatabase { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("UNIQUE") { - DatabaseError::CategoryAlreadyExists + database::Error::CategoryAlreadyExists } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error, + _ => database::Error::Error, }) } - async fn get_category_from_id(&self, category_id: i64) -> Result { + async fn get_category_from_id(&self, category_id: i64) -> Result { query_as::<_, Category>("SELECT category_id, name, (SELECT COUNT(*) FROM torrust_torrents WHERE torrust_torrents.category_id = torrust_categories.category_id) AS num_torrents FROM torrust_categories WHERE category_id = ?") .bind(category_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::CategoryNotFound) + .map_err(|_| database::Error::CategoryNotFound) } - async fn get_category_from_name(&self, category_name: &str) -> Result { + async fn get_category_from_name(&self, category_name: &str) -> Result { query_as::<_, Category>("SELECT category_id, name, (SELECT COUNT(*) FROM torrust_torrents WHERE torrust_torrents.category_id = torrust_categories.category_id) AS num_torrents FROM torrust_categories WHERE name = ?") .bind(category_name) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::CategoryNotFound) + .map_err(|_| database::Error::CategoryNotFound) } - async fn get_categories(&self) -> Result, DatabaseError> { + async fn get_categories(&self) -> Result, database::Error> { query_as::<_, Category>("SELECT tc.category_id, tc.name, COUNT(tt.category_id) as num_torrents FROM torrust_categories tc LEFT JOIN torrust_torrents tt on tc.category_id = tt.category_id GROUP BY tc.name") .fetch_all(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn delete_category(&self, category_name: &str) -> Result<(), DatabaseError> { + async fn delete_category(&self, category_name: &str) -> Result<(), database::Error> { query("DELETE FROM torrust_categories WHERE name = ?") .bind(category_name) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::CategoryNotFound) + Err(database::Error::CategoryNotFound) } }) } @@ -286,7 +287,7 @@ impl Database for SqliteDatabase { sort: &Sorting, offset: u64, limit: u8, - ) -> Result { + ) -> Result { let title = match search { None => "%".to_string(), Some(v) => format!("%{}%", v), @@ -346,12 +347,12 @@ impl Database for SqliteDatabase { let count_query = format!("SELECT COUNT(*) as count FROM ({}) AS count_table", query_string); - let count_result: Result = query_as(&count_query) + let count_result: Result = query_as(&count_query) .bind(title.clone()) .fetch_one(&self.pool) .await .map(|(v,)| v) - .map_err(|_| DatabaseError::Error); + .map_err(|_| database::Error::Error); let count = count_result?; @@ -363,7 +364,7 @@ impl Database for SqliteDatabase { .bind(limit) .fetch_all(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; Ok(TorrentsResponse { total: count as u32, @@ -378,20 +379,20 @@ impl Database for SqliteDatabase { category_id: i64, title: &str, description: &str, - ) -> Result { + ) -> Result { let info_hash = torrent.info_hash(); // open pool connection - let mut conn = self.pool.acquire().await.map_err(|_| DatabaseError::Error)?; + let mut conn = self.pool.acquire().await.map_err(|_| database::Error::Error)?; // start db transaction - let mut tx = conn.begin().await.map_err(|_| DatabaseError::Error)?; + let mut tx = conn.begin().await.map_err(|_| database::Error::Error)?; // torrent file can only hold a pieces key or a root hash key: http://www.bittorrent.org/beps/bep_0030.html let (pieces, root_hash): (String, bool) = if let Some(pieces) = &torrent.info.pieces { (bytes_to_hex(pieces.as_ref()), false) } else { - let root_hash = torrent.info.root_hash.as_ref().ok_or(DatabaseError::Error)?; + let root_hash = torrent.info.root_hash.as_ref().ok_or(database::Error::Error)?; (root_hash.to_string(), true) }; @@ -414,14 +415,14 @@ impl Database for SqliteDatabase { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("info_hash") { - DatabaseError::TorrentAlreadyExists + database::Error::TorrentAlreadyExists } else if err.message().contains("title") { - DatabaseError::TorrentTitleAlreadyExists + database::Error::TorrentTitleAlreadyExists } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error + _ => database::Error::Error })?; let insert_torrent_files_result = if let Some(length) = torrent.info.length { @@ -432,7 +433,7 @@ impl Database for SqliteDatabase { .execute(&mut tx) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } else { let files = torrent.info.files.as_ref().unwrap(); @@ -446,7 +447,7 @@ impl Database for SqliteDatabase { .bind(path) .execute(&mut tx) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; } Ok(()) @@ -458,7 +459,8 @@ impl Database for SqliteDatabase { return Err(e); } - let insert_torrent_announce_urls_result: Result<(), DatabaseError> = if let Some(announce_urls) = &torrent.announce_list { + let insert_torrent_announce_urls_result: Result<(), database::Error> = if let Some(announce_urls) = &torrent.announce_list + { // flatten the nested vec (this will however remove the) let announce_urls = announce_urls.iter().flatten().collect::>(); @@ -469,7 +471,7 @@ impl Database for SqliteDatabase { .execute(&mut tx) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; } Ok(()) @@ -482,7 +484,7 @@ impl Database for SqliteDatabase { .execute(&mut tx) .await .map(|_| ()) - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) }; // rollback transaction on error @@ -501,14 +503,14 @@ impl Database for SqliteDatabase { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("info_hash") { - DatabaseError::TorrentAlreadyExists + database::Error::TorrentAlreadyExists } else if err.message().contains("title") { - DatabaseError::TorrentTitleAlreadyExists + database::Error::TorrentTitleAlreadyExists } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error, + _ => database::Error::Error, }); // commit or rollback transaction and return user_id on success @@ -524,33 +526,33 @@ impl Database for SqliteDatabase { } } - async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result { + async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result { query_as::<_, DbTorrentInfo>( "SELECT torrent_id, info_hash, name, pieces, piece_length, private, root_hash FROM torrust_torrents WHERE torrent_id = ?", ) .bind(torrent_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn get_torrent_info_from_infohash(&self, infohash: &InfoHash) -> Result { + async fn get_torrent_info_from_infohash(&self, infohash: &InfoHash) -> Result { query_as::<_, DbTorrentInfo>( "SELECT torrent_id, info_hash, name, pieces, piece_length, private, root_hash FROM torrust_torrents WHERE info_hash = ?", ) .bind(infohash.to_hex_string().to_uppercase()) // `info_hash` is stored as uppercase hex string .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn get_torrent_files_from_id(&self, torrent_id: i64) -> Result, DatabaseError> { + async fn get_torrent_files_from_id(&self, torrent_id: i64) -> Result, database::Error> { let db_torrent_files = query_as::<_, DbTorrentFile>("SELECT md5sum, length, path FROM torrust_torrent_files WHERE torrent_id = ?") .bind(torrent_id) .fetch_all(&self.pool) .await - .map_err(|_| DatabaseError::TorrentNotFound)?; + .map_err(|_| database::Error::TorrentNotFound)?; let torrent_files: Vec = db_torrent_files .into_iter() @@ -569,16 +571,16 @@ impl Database for SqliteDatabase { Ok(torrent_files) } - async fn get_torrent_announce_urls_from_id(&self, torrent_id: i64) -> Result>, DatabaseError> { + async fn get_torrent_announce_urls_from_id(&self, torrent_id: i64) -> Result>, database::Error> { query_as::<_, DbTorrentAnnounceUrl>("SELECT tracker_url FROM torrust_torrent_announce_urls WHERE torrent_id = ?") .bind(torrent_id) .fetch_all(&self.pool) .await .map(|v| v.iter().map(|a| vec![a.tracker_url.to_string()]).collect()) - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result { + async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result { query_as::<_, TorrentListing>( "SELECT tt.torrent_id, tp.username AS uploader, tt.info_hash, ti.title, ti.description, tt.category_id, tt.date_uploaded, tt.size AS file_size, CAST(COALESCE(sum(ts.seeders),0) as signed) as seeders, @@ -593,10 +595,10 @@ impl Database for SqliteDatabase { .bind(torrent_id) .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn get_torrent_listing_from_infohash(&self, infohash: &InfoHash) -> Result { + async fn get_torrent_listing_from_infohash(&self, infohash: &InfoHash) -> Result { query_as::<_, TorrentListing>( "SELECT tt.torrent_id, tp.username AS uploader, tt.info_hash, ti.title, ti.description, tt.category_id, tt.date_uploaded, tt.size AS file_size, CAST(COALESCE(sum(ts.seeders),0) as signed) as seeders, @@ -611,17 +613,17 @@ impl Database for SqliteDatabase { .bind(infohash.to_string().to_uppercase()) // `info_hash` is stored as uppercase .fetch_one(&self.pool) .await - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn get_all_torrents_compact(&self) -> Result, DatabaseError> { + async fn get_all_torrents_compact(&self) -> Result, database::Error> { query_as::<_, TorrentCompact>("SELECT torrent_id, info_hash FROM torrust_torrents") .fetch_all(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - async fn update_torrent_title(&self, torrent_id: i64, title: &str) -> Result<(), DatabaseError> { + async fn update_torrent_title(&self, torrent_id: i64, title: &str) -> Result<(), database::Error> { query("UPDATE torrust_torrent_info SET title = $1 WHERE torrent_id = $2") .bind(title) .bind(torrent_id) @@ -630,34 +632,34 @@ impl Database for SqliteDatabase { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("UNIQUE") { - DatabaseError::TorrentTitleAlreadyExists + database::Error::TorrentTitleAlreadyExists } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error, + _ => database::Error::Error, }) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::TorrentNotFound) + Err(database::Error::TorrentNotFound) } }) } - async fn update_torrent_description(&self, torrent_id: i64, description: &str) -> Result<(), DatabaseError> { + async fn update_torrent_description(&self, torrent_id: i64, description: &str) -> Result<(), database::Error> { query("UPDATE torrust_torrent_info SET description = $1 WHERE torrent_id = $2") .bind(description) .bind(torrent_id) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::TorrentNotFound) + Err(database::Error::TorrentNotFound) } }) } @@ -668,7 +670,7 @@ impl Database for SqliteDatabase { tracker_url: &str, seeders: i64, leechers: i64, - ) -> Result<(), DatabaseError> { + ) -> Result<(), database::Error> { query("REPLACE INTO torrust_torrent_tracker_stats (torrent_id, tracker_url, seeders, leechers) VALUES ($1, $2, $3, $4)") .bind(torrent_id) .bind(tracker_url) @@ -677,74 +679,74 @@ impl Database for SqliteDatabase { .execute(&self.pool) .await .map(|_| ()) - .map_err(|_| DatabaseError::TorrentNotFound) + .map_err(|_| database::Error::TorrentNotFound) } - async fn delete_torrent(&self, torrent_id: i64) -> Result<(), DatabaseError> { + async fn delete_torrent(&self, torrent_id: i64) -> Result<(), database::Error> { query("DELETE FROM torrust_torrents WHERE torrent_id = ?") .bind(torrent_id) .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) .and_then(|v| { if v.rows_affected() > 0 { Ok(()) } else { - Err(DatabaseError::TorrentNotFound) + Err(database::Error::TorrentNotFound) } }) } - async fn delete_all_database_rows(&self) -> Result<(), DatabaseError> { + async fn delete_all_database_rows(&self) -> Result<(), database::Error> { query("DELETE FROM torrust_categories;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_torrents;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_tracker_keys;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_users;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_user_authentication;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_user_bans;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_user_invitations;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_user_profiles;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_torrents;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; query("DELETE FROM torrust_user_public_keys;") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error)?; + .map_err(|_| database::Error::Error)?; Ok(()) } diff --git a/src/errors.rs b/src/errors.rs index a4eb7943..84fe7c8a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -6,7 +6,7 @@ use actix_web::{HttpResponse, HttpResponseBuilder, ResponseError}; use derive_more::{Display, Error}; use serde::{Deserialize, Serialize}; -use crate::databases::database::DatabaseError; +use crate::databases::database; pub type ServiceResult = Result; @@ -210,19 +210,19 @@ impl From for ServiceError { } } -impl From for ServiceError { - fn from(e: DatabaseError) -> Self { +impl From for ServiceError { + fn from(e: database::Error) -> Self { match e { - DatabaseError::Error => ServiceError::InternalServerError, - DatabaseError::UsernameTaken => ServiceError::UsernameTaken, - DatabaseError::EmailTaken => ServiceError::EmailTaken, - DatabaseError::UserNotFound => ServiceError::UserNotFound, - DatabaseError::CategoryAlreadyExists => ServiceError::CategoryExists, - DatabaseError::CategoryNotFound => ServiceError::InvalidCategory, - DatabaseError::TorrentNotFound => ServiceError::TorrentNotFound, - DatabaseError::TorrentAlreadyExists => ServiceError::InfoHashAlreadyExists, - DatabaseError::TorrentTitleAlreadyExists => ServiceError::TorrentTitleAlreadyExists, - DatabaseError::UnrecognizedDatabaseDriver => ServiceError::InternalServerError, + database::Error::Error => ServiceError::InternalServerError, + database::Error::UsernameTaken => ServiceError::UsernameTaken, + database::Error::EmailTaken => ServiceError::EmailTaken, + database::Error::UserNotFound => ServiceError::UserNotFound, + database::Error::CategoryAlreadyExists => ServiceError::CategoryExists, + database::Error::CategoryNotFound => ServiceError::InvalidCategory, + database::Error::TorrentNotFound => ServiceError::TorrentNotFound, + database::Error::TorrentAlreadyExists => ServiceError::InfoHashAlreadyExists, + database::Error::TorrentTitleAlreadyExists => ServiceError::TorrentTitleAlreadyExists, + database::Error::UnrecognizedDatabaseDriver => ServiceError::InternalServerError, } } } diff --git a/src/tracker/statistics_importer.rs b/src/tracker/statistics_importer.rs index 76c9d485..c5044c5c 100644 --- a/src/tracker/statistics_importer.rs +++ b/src/tracker/statistics_importer.rs @@ -4,7 +4,7 @@ use log::{error, info}; use super::service::{Service, TorrentInfo}; use crate::config::Configuration; -use crate::databases::database::{Database, DatabaseError}; +use crate::databases::database::{self, Database}; use crate::errors::ServiceError; pub struct StatisticsImporter { @@ -30,7 +30,7 @@ impl StatisticsImporter { /// # Errors /// /// Will return an error if the database query failed. - pub async fn import_all_torrents_statistics(&self) -> Result<(), DatabaseError> { + pub async fn import_all_torrents_statistics(&self) -> Result<(), database::Error> { info!("Importing torrents statistics from tracker ..."); let torrents = self.database.get_all_torrents_compact().await?; diff --git a/src/upgrades/from_v1_0_0_to_v2_0_0/databases/sqlite_v1_0_0.rs b/src/upgrades/from_v1_0_0_to_v2_0_0/databases/sqlite_v1_0_0.rs index 1f4987c6..8d58d5a4 100644 --- a/src/upgrades/from_v1_0_0_to_v2_0_0/databases/sqlite_v1_0_0.rs +++ b/src/upgrades/from_v1_0_0_to_v2_0_0/databases/sqlite_v1_0_0.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use sqlx::sqlite::SqlitePoolOptions; use sqlx::{query_as, SqlitePool}; -use crate::databases::database::DatabaseError; +use crate::databases::database; #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct CategoryRecordV1 { @@ -64,11 +64,11 @@ impl SqliteDatabaseV1_0_0 { Self { pool: db } } - pub async fn get_categories_order_by_id(&self) -> Result, DatabaseError> { + pub async fn get_categories_order_by_id(&self) -> Result, database::Error> { query_as::<_, CategoryRecordV1>("SELECT category_id, name FROM torrust_categories ORDER BY category_id ASC") .fetch_all(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } pub async fn get_users(&self) -> Result, sqlx::Error> { diff --git a/src/upgrades/from_v1_0_0_to_v2_0_0/databases/sqlite_v2_0_0.rs b/src/upgrades/from_v1_0_0_to_v2_0_0/databases/sqlite_v2_0_0.rs index 9107356b..d182efaf 100644 --- a/src/upgrades/from_v1_0_0_to_v2_0_0/databases/sqlite_v2_0_0.rs +++ b/src/upgrades/from_v1_0_0_to_v2_0_0/databases/sqlite_v2_0_0.rs @@ -4,7 +4,7 @@ use sqlx::sqlite::{SqlitePoolOptions, SqliteQueryResult}; use sqlx::{query, query_as, SqlitePool}; use super::sqlite_v1_0_0::{TorrentRecordV1, UserRecordV1}; -use crate::databases::database::DatabaseError; +use crate::databases::database; use crate::models::torrent_file::{TorrentFile, TorrentInfo}; #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] @@ -79,21 +79,21 @@ impl SqliteDatabaseV2_0_0 { .expect("Could not run database migrations."); } - pub async fn reset_categories_sequence(&self) -> Result { + pub async fn reset_categories_sequence(&self) -> Result { query("DELETE FROM `sqlite_sequence` WHERE `name` = 'torrust_categories'") .execute(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - pub async fn get_categories(&self) -> Result, DatabaseError> { + pub async fn get_categories(&self) -> Result, database::Error> { query_as::<_, CategoryRecordV2>("SELECT tc.category_id, tc.name, COUNT(tt.category_id) as num_torrents FROM torrust_categories tc LEFT JOIN torrust_torrents tt on tc.category_id = tt.category_id GROUP BY tc.name") .fetch_all(&self.pool) .await - .map_err(|_| DatabaseError::Error) + .map_err(|_| database::Error::Error) } - pub async fn insert_category_and_get_id(&self, category_name: &str) -> Result { + pub async fn insert_category_and_get_id(&self, category_name: &str) -> Result { query("INSERT INTO torrust_categories (name) VALUES (?)") .bind(category_name) .execute(&self.pool) @@ -102,12 +102,12 @@ impl SqliteDatabaseV2_0_0 { .map_err(|e| match e { sqlx::Error::Database(err) => { if err.message().contains("UNIQUE") { - DatabaseError::CategoryAlreadyExists + database::Error::CategoryAlreadyExists } else { - DatabaseError::Error + database::Error::Error } } - _ => DatabaseError::Error, + _ => database::Error::Error, }) } @@ -257,7 +257,7 @@ impl SqliteDatabaseV2_0_0 { .map(|v| v.last_insert_rowid()) } - pub async fn delete_all_database_rows(&self) -> Result<(), DatabaseError> { + pub async fn delete_all_database_rows(&self) -> Result<(), database::Error> { query("DELETE FROM torrust_categories").execute(&self.pool).await.unwrap(); query("DELETE FROM torrust_torrents").execute(&self.pool).await.unwrap(); diff --git a/tests/databases/mod.rs b/tests/databases/mod.rs index c5a03519..07607c68 100644 --- a/tests/databases/mod.rs +++ b/tests/databases/mod.rs @@ -1,6 +1,7 @@ use std::future::Future; -use torrust_index_backend::databases::database::{connect_database, Database}; +use torrust_index_backend::databases::database; +use torrust_index_backend::databases::database::Database; mod mysql; mod sqlite; @@ -21,7 +22,7 @@ where // runs all tests pub async fn run_tests(db_path: &str) { - let db_res = connect_database(db_path).await; + let db_res = database::connect(db_path).await; assert!(db_res.is_ok()); diff --git a/tests/databases/tests.rs b/tests/databases/tests.rs index 834fdc47..f500164f 100644 --- a/tests/databases/tests.rs +++ b/tests/databases/tests.rs @@ -1,5 +1,6 @@ use serde_bytes::ByteBuf; -use torrust_index_backend::databases::database::{Database, DatabaseError}; +use torrust_index_backend::databases::database; +use torrust_index_backend::databases::database::Database; use torrust_index_backend::models::torrent::TorrentListing; use torrust_index_backend::models::torrent_file::{Torrent, TorrentInfo}; use torrust_index_backend::models::user::UserProfile; @@ -19,12 +20,12 @@ const TEST_TORRENT_FILE_SIZE: i64 = 128_000; const TEST_TORRENT_SEEDERS: i64 = 437; const TEST_TORRENT_LEECHERS: i64 = 1289; -async fn add_test_user(db: &Box) -> Result { +async fn add_test_user(db: &Box) -> Result { db.insert_user_and_get_id(TEST_USER_USERNAME, TEST_USER_EMAIL, TEST_USER_PASSWORD) .await } -async fn add_test_torrent_category(db: &Box) -> Result { +async fn add_test_torrent_category(db: &Box) -> Result { db.insert_category_and_get_id(TEST_CATEGORY_NAME).await } diff --git a/tests/e2e/contexts/torrent/asserts.rs b/tests/e2e/contexts/torrent/asserts.rs index c5c9759f..6e191a9d 100644 --- a/tests/e2e/contexts/torrent/asserts.rs +++ b/tests/e2e/contexts/torrent/asserts.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use torrust_index_backend::databases::database::connect_database; +use torrust_index_backend::databases::database; use torrust_index_backend::models::torrent_file::Torrent; use torrust_index_backend::models::tracker_key::TrackerKey; @@ -41,7 +41,7 @@ pub async fn get_user_tracker_key(logged_in_user: &LoggedInUserData, env: &TestE // querying the database. let database = Arc::new( - connect_database(&env.database_connect_url().unwrap()) + database::connect(&env.database_connect_url().unwrap()) .await .expect("database connection to be established."), ); diff --git a/tests/e2e/contexts/user/steps.rs b/tests/e2e/contexts/user/steps.rs index 96627340..c58a5c59 100644 --- a/tests/e2e/contexts/user/steps.rs +++ b/tests/e2e/contexts/user/steps.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use torrust_index_backend::databases::database::connect_database; +use torrust_index_backend::databases::database; use crate::common::client::Client; use crate::common::contexts::user::fixtures::random_user_registration; @@ -12,7 +12,7 @@ pub async fn new_logged_in_admin(env: &TestEnv) -> LoggedInUserData { let user = new_logged_in_user(env).await; let database = Arc::new( - connect_database(&env.database_connect_url().unwrap()) + database::connect(&env.database_connect_url().unwrap()) .await .expect("Database error."), );