use async_trait::async_trait;
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use crate::databases::mysql::Mysql;
use crate::databases::sqlite::Sqlite;
use crate::models::info_hash::InfoHash;
use crate::models::response::TorrentsResponse;
use crate::models::torrent::TorrentListing;
use crate::models::torrent_file::{DbTorrentInfo, Torrent, TorrentFile};
use crate::models::tracker_key::TrackerKey;
use crate::models::user::{User, UserAuthentication, UserCompact, UserProfile};
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
pub enum Driver {
Sqlite3,
Mysql,
}
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct TorrentCompact {
pub torrent_id: i64,
pub info_hash: String,
}
#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
pub struct Category {
pub category_id: i64,
pub name: String,
pub num_torrents: i64,
}
#[derive(Clone, Copy, Debug, Deserialize)]
pub enum Sorting {
UploadedAsc,
UploadedDesc,
SeedersAsc,
SeedersDesc,
LeechersAsc,
LeechersDesc,
NameAsc,
NameDesc,
SizeAsc,
SizeDesc,
}
#[derive(Debug)]
pub enum Error {
Error,
UnrecognizedDatabaseDriver, UsernameTaken,
EmailTaken,
UserNotFound,
CategoryAlreadyExists,
CategoryNotFound,
TorrentNotFound,
TorrentAlreadyExists, TorrentTitleAlreadyExists,
}
pub fn get_driver(db_path: &str) -> Result<Driver, Error> {
match &db_path.chars().collect::<Vec<char>>() as &[char] {
['s', 'q', 'l', 'i', 't', 'e', ..] => Ok(Driver::Sqlite3),
['m', 'y', 's', 'q', 'l', ..] => Ok(Driver::Mysql),
_ => Err(Error::UnrecognizedDatabaseDriver),
}
}
pub async fn connect(db_path: &str) -> Result<Box<dyn Database>, Error> {
let db_driver = self::get_driver(db_path)?;
Ok(match db_driver {
self::Driver::Sqlite3 => Box::new(Sqlite::new(db_path).await),
self::Driver::Mysql => Box::new(Mysql::new(db_path).await),
})
}
#[async_trait]
pub trait Database: Sync + Send {
fn get_database_driver(&self) -> Driver;
async fn new(db_path: &str) -> Self
where
Self: Sized;
async fn insert_user_and_get_id(&self, username: &str, email: &str, password: &str) -> Result<i64, Error>;
async fn get_user_from_id(&self, user_id: i64) -> Result<User, Error>;
async fn get_user_authentication_from_id(&self, user_id: i64) -> Result<UserAuthentication, Error>;
async fn get_user_profile_from_username(&self, username: &str) -> Result<UserProfile, Error>;
async fn get_user_compact_from_id(&self, user_id: i64) -> Result<UserCompact, Error>;
async fn get_user_tracker_key(&self, user_id: i64) -> Option<TrackerKey>;
async fn count_users(&self) -> Result<i64, Error>;
async fn ban_user(&self, user_id: i64, reason: &str, date_expiry: NaiveDateTime) -> Result<(), Error>;
async fn grant_admin_role(&self, user_id: i64) -> Result<(), Error>;
async fn verify_email(&self, user_id: i64) -> Result<(), Error>;
async fn add_tracker_key(&self, user_id: i64, tracker_key: &TrackerKey) -> Result<(), Error>;
async fn delete_user(&self, user_id: i64) -> Result<(), Error>;
async fn insert_category_and_get_id(&self, category_name: &str) -> Result<i64, Error>;
async fn get_category_from_id(&self, category_id: i64) -> Result<Category, Error>;
async fn get_category_from_name(&self, category_name: &str) -> Result<Category, Error>;
async fn get_categories(&self) -> Result<Vec<Category>, Error>;
async fn delete_category(&self, category_name: &str) -> Result<(), Error>;
async fn get_torrents_search_sorted_paginated(
&self,
search: &Option<String>,
categories: &Option<Vec<String>>,
sort: &Sorting,
offset: u64,
page_size: u8,
) -> Result<TorrentsResponse, Error>;
async fn insert_torrent_and_get_id(
&self,
torrent: &Torrent,
uploader_id: i64,
category_id: i64,
title: &str,
description: &str,
) -> Result<i64, Error>;
async fn get_torrent_from_info_hash(&self, info_hash: &InfoHash) -> Result<Torrent, Error> {
let torrent_info = self.get_torrent_info_from_info_hash(info_hash).await?;
let torrent_files = self.get_torrent_files_from_id(torrent_info.torrent_id).await?;
let torrent_announce_urls = self.get_torrent_announce_urls_from_id(torrent_info.torrent_id).await?;
Ok(Torrent::from_db_info_files_and_announce_urls(
torrent_info,
torrent_files,
torrent_announce_urls,
))
}
async fn get_torrent_from_id(&self, torrent_id: i64) -> Result<Torrent, Error> {
let torrent_info = self.get_torrent_info_from_id(torrent_id).await?;
let torrent_files = self.get_torrent_files_from_id(torrent_id).await?;
let torrent_announce_urls = self.get_torrent_announce_urls_from_id(torrent_id).await?;
Ok(Torrent::from_db_info_files_and_announce_urls(
torrent_info,
torrent_files,
torrent_announce_urls,
))
}
async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result<DbTorrentInfo, Error>;
async fn get_torrent_info_from_info_hash(&self, info_hash: &InfoHash) -> Result<DbTorrentInfo, Error>;
async fn get_torrent_files_from_id(&self, torrent_id: i64) -> Result<Vec<TorrentFile>, Error>;
async fn get_torrent_announce_urls_from_id(&self, torrent_id: i64) -> Result<Vec<Vec<String>>, Error>;
async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result<TorrentListing, Error>;
async fn get_torrent_listing_from_info_hash(&self, info_hash: &InfoHash) -> Result<TorrentListing, Error>;
async fn get_all_torrents_compact(&self) -> Result<Vec<TorrentCompact>, Error>;
async fn update_torrent_title(&self, torrent_id: i64, title: &str) -> Result<(), Error>;
async fn update_torrent_description(&self, torrent_id: i64, description: &str) -> Result<(), Error>;
async fn update_tracker_info(&self, torrent_id: i64, tracker_url: &str, seeders: i64, leechers: i64) -> Result<(), Error>;
async fn delete_torrent(&self, torrent_id: i64) -> Result<(), Error>;
async fn delete_all_database_rows(&self) -> Result<(), Error>;
}