Skip to content

Commit 4150139

Browse files
committed
dev: located error for database
1 parent aa0f371 commit 4150139

File tree

6 files changed

+329
-267
lines changed

6 files changed

+329
-267
lines changed

src/databases/driver.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,30 @@
11
use serde::{Deserialize, Serialize};
22

3-
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
3+
use super::error::Error;
4+
use super::mysql::Mysql;
5+
use super::sqlite::Sqlite;
6+
use super::{Builder, Database};
7+
8+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, derive_more::Display, Clone)]
49
pub enum Driver {
510
Sqlite3,
611
MySQL,
712
}
13+
14+
impl Driver {
15+
/// .
16+
///
17+
/// # Errors
18+
///
19+
/// This function will return an error if unable to connect to the database.
20+
pub fn build(&self, db_path: &str) -> Result<Box<dyn Database>, Error> {
21+
let database = match self {
22+
Driver::Sqlite3 => Builder::<Sqlite>::build(db_path),
23+
Driver::MySQL => Builder::<Mysql>::build(db_path),
24+
}?;
25+
26+
database.create_database_tables().expect("Could not create database tables.");
27+
28+
Ok(database)
29+
}
30+
}

src/databases/error.rs

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,95 @@
1-
use derive_more::{Display, Error};
1+
use std::panic::Location;
2+
use std::sync::Arc;
23

3-
#[derive(Debug, Display, PartialEq, Eq, Error)]
4-
#[allow(dead_code)]
4+
use r2d2_mysql::mysql::UrlError;
5+
6+
use super::driver::Driver;
7+
use crate::located_error::{Located, LocatedError};
8+
9+
#[derive(thiserror::Error, Debug, Clone)]
510
pub enum Error {
6-
#[display(fmt = "Query returned no rows.")]
7-
QueryReturnedNoRows,
8-
#[display(fmt = "Invalid query.")]
9-
InvalidQuery,
10-
#[display(fmt = "Database error.")]
11-
DatabaseError,
11+
#[error("The {driver} query unexpectedly returned nothing: {source}")]
12+
QueryReturnedNoRows {
13+
source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
14+
driver: Driver,
15+
},
16+
17+
#[error("The {driver} query was malformed: {source}")]
18+
InvalidQuery {
19+
source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
20+
driver: Driver,
21+
},
22+
23+
#[error("Unable to insert record into {driver} database, {location}")]
24+
InsertFailed {
25+
location: &'static Location<'static>,
26+
driver: Driver,
27+
},
28+
29+
#[error("Failed to remove record from {driver} database, error-code: {error_code}, {location}")]
30+
DeleteFailed {
31+
location: &'static Location<'static>,
32+
error_code: usize,
33+
driver: Driver,
34+
},
35+
36+
#[error("Failed to connect to {driver} database: {source}")]
37+
ConnectionError {
38+
source: LocatedError<'static, UrlError>,
39+
driver: Driver,
40+
},
41+
42+
#[error("Failed to create r2d2 {driver} connection pool: {source}")]
43+
ConnectionPool {
44+
source: LocatedError<'static, r2d2::Error>,
45+
driver: Driver,
46+
},
1247
}
1348

1449
impl From<r2d2_sqlite::rusqlite::Error> for Error {
15-
fn from(e: r2d2_sqlite::rusqlite::Error) -> Self {
16-
match e {
17-
r2d2_sqlite::rusqlite::Error::QueryReturnedNoRows => Error::QueryReturnedNoRows,
18-
_ => Error::InvalidQuery,
50+
#[track_caller]
51+
fn from(err: r2d2_sqlite::rusqlite::Error) -> Self {
52+
match err {
53+
r2d2_sqlite::rusqlite::Error::QueryReturnedNoRows => Error::QueryReturnedNoRows {
54+
source: (Arc::new(err) as Arc<dyn std::error::Error + Send + Sync>).into(),
55+
driver: Driver::Sqlite3,
56+
},
57+
_ => Error::InvalidQuery {
58+
source: (Arc::new(err) as Arc<dyn std::error::Error + Send + Sync>).into(),
59+
driver: Driver::Sqlite3,
60+
},
61+
}
62+
}
63+
}
64+
65+
impl From<r2d2_mysql::mysql::Error> for Error {
66+
#[track_caller]
67+
fn from(err: r2d2_mysql::mysql::Error) -> Self {
68+
let e: Arc<dyn std::error::Error + Send + Sync> = Arc::new(err);
69+
Error::InvalidQuery {
70+
source: e.into(),
71+
driver: Driver::MySQL,
72+
}
73+
}
74+
}
75+
76+
impl From<UrlError> for Error {
77+
#[track_caller]
78+
fn from(err: UrlError) -> Self {
79+
Self::ConnectionError {
80+
source: Located(err).into(),
81+
driver: Driver::MySQL,
82+
}
83+
}
84+
}
85+
86+
impl From<(r2d2::Error, Driver)> for Error {
87+
#[track_caller]
88+
fn from(e: (r2d2::Error, Driver)) -> Self {
89+
let (err, driver) = e;
90+
Self::ConnectionPool {
91+
source: Located(err).into(),
92+
driver,
1993
}
2094
}
2195
}

src/databases/mod.rs

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,48 @@ pub mod error;
33
pub mod mysql;
44
pub mod sqlite;
55

6+
use std::marker::PhantomData;
7+
68
use async_trait::async_trait;
79

8-
use self::driver::Driver;
910
use self::error::Error;
10-
use crate::databases::mysql::Mysql;
11-
use crate::databases::sqlite::Sqlite;
1211
use crate::protocol::info_hash::InfoHash;
1312
use crate::tracker::auth;
1413

15-
/// # Errors
16-
///
17-
/// Will return `r2d2::Error` if `db_path` is not able to create a database.
18-
pub fn connect(db_driver: &Driver, db_path: &str) -> Result<Box<dyn Database>, r2d2::Error> {
19-
let database: Box<dyn Database> = match db_driver {
20-
Driver::Sqlite3 => {
21-
let db = Sqlite::new(db_path)?;
22-
Box::new(db)
23-
}
24-
Driver::MySQL => {
25-
let db = Mysql::new(db_path)?;
26-
Box::new(db)
27-
}
28-
};
29-
30-
database.create_database_tables().expect("Could not create database tables.");
31-
32-
Ok(database)
14+
pub(self) struct Builder<T>
15+
where
16+
T: Database,
17+
{
18+
phantom: PhantomData<T>,
19+
}
20+
21+
impl<T> Builder<T>
22+
where
23+
T: Database + 'static,
24+
{
25+
/// .
26+
///
27+
/// # Errors
28+
///
29+
/// Will return `r2d2::Error` if `db_path` is not able to create a database.
30+
pub(self) fn build(db_path: &str) -> Result<Box<dyn Database>, Error> {
31+
Ok(Box::new(T::new(db_path)?))
32+
}
3333
}
3434

3535
#[async_trait]
3636
pub trait Database: Sync + Send {
37+
/// .
38+
///
39+
/// # Errors
40+
///
41+
/// Will return `r2d2::Error` if `db_path` is not able to create a database.
42+
fn new(db_path: &str) -> Result<Self, Error>
43+
where
44+
Self: std::marker::Sized;
45+
46+
/// .
47+
///
3748
/// # Errors
3849
///
3950
/// Will return `Error` if unable to create own tables.
@@ -52,27 +63,22 @@ pub trait Database: Sync + Send {
5263

5364
async fn save_persistent_torrent(&self, info_hash: &InfoHash, completed: u32) -> Result<(), Error>;
5465

55-
async fn get_info_hash_from_whitelist(&self, info_hash: &str) -> Result<InfoHash, Error>;
66+
async fn get_info_hash_from_whitelist(&self, info_hash: &str) -> Result<Option<InfoHash>, Error>;
5667

5768
async fn add_info_hash_to_whitelist(&self, info_hash: InfoHash) -> Result<usize, Error>;
5869

5970
async fn remove_info_hash_from_whitelist(&self, info_hash: InfoHash) -> Result<usize, Error>;
6071

61-
async fn get_key_from_keys(&self, key: &str) -> Result<auth::Key, Error>;
72+
async fn get_key_from_keys(&self, key: &str) -> Result<Option<auth::Key>, Error>;
6273

6374
async fn add_key_to_keys(&self, auth_key: &auth::Key) -> Result<usize, Error>;
6475

6576
async fn remove_key_from_keys(&self, key: &str) -> Result<usize, Error>;
6677

6778
async fn is_info_hash_whitelisted(&self, info_hash: &InfoHash) -> Result<bool, Error> {
68-
self.get_info_hash_from_whitelist(&info_hash.clone().to_string())
69-
.await
70-
.map_or_else(
71-
|e| match e {
72-
Error::QueryReturnedNoRows => Ok(false),
73-
e => Err(e),
74-
},
75-
|_| Ok(true),
76-
)
79+
Ok(self
80+
.get_info_hash_from_whitelist(&info_hash.clone().to_string())
81+
.await?
82+
.is_some())
7783
}
7884
}

0 commit comments

Comments
 (0)