From e5eb7b743505eb562653ffb56ef01ab639c60915 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Thu, 21 Oct 2021 03:01:00 +0300 Subject: [PATCH] feat: add r2d2_sqlite feature --- .vscode/settings.json | 3 +- Cargo.lock | 83 +++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/error.rs | 20 ++++++--- src/lib.rs | 12 ++++- src/r2d2_postgres.rs | 4 +- src/r2d2_sqlite.rs | 100 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 213 insertions(+), 11 deletions(-) create mode 100644 src/r2d2_sqlite.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 0ac6bc2..993067d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "rust.unstable_features": true + "rust.unstable_features": true, + "rust.all_features": true, } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3b3e1a3..3c9d546 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -156,6 +167,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fnv" version = "1.0.7" @@ -277,6 +294,24 @@ dependencies = [ "wasi", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +dependencies = [ + "hashbrown", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -409,6 +444,16 @@ version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" +[[package]] +name = "libsqlite3-sys" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd5850c449b40bacb498b2bbdfaff648b1b055630073ba8db499caf2d0ea9f2" +dependencies = [ + "pkg-config", + "vcpkg", +] + [[package]] name = "lock_api" version = "0.4.5" @@ -500,6 +545,7 @@ dependencies = [ "bb8-postgres", "r2d2", "r2d2_postgres", + "r2d2_sqlite", ] [[package]] @@ -569,6 +615,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb" + [[package]] name = "postgres" version = "0.19.2" @@ -687,6 +739,16 @@ dependencies = [ "r2d2", ] +[[package]] +name = "r2d2_sqlite" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ca3c9468a76fc2ad724c486a59682fc362efeac7b18d1c012958bc19f34800" +dependencies = [ + "r2d2", + "rusqlite", +] + [[package]] name = "rand" version = "0.8.4" @@ -753,6 +815,21 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "rusqlite" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a82b0b91fad72160c56bf8da7a549b25d7c31109f52cc1437eac4c0ad2550a7" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "memchr", + "smallvec", +] + [[package]] name = "ryu" version = "1.0.5" @@ -1033,6 +1110,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index 2e53d44..42df2d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ sync = [] bb8_postgres = ["async", "bb8", "bb8-postgres"] r2d2_postgres = ["sync", "r2d2", "r2d2-postgres"] +r2d2_sqlite = ["sync", "r2d2", "r2d2-sqlite"] [dependencies] async-trait = { version = "0.1", optional = true } @@ -27,6 +28,7 @@ bb8-postgres = { version = "0.7", optional = true } r2d2 = { version = "0.8", optional = true } r2d2-postgres = { package = "r2d2_postgres", version = "0.18", optional = true } +r2d2-sqlite = { package = "r2d2_sqlite", version = "0.19", optional = true } [package.metadata.docs.rs] all-features = true diff --git a/src/error.rs b/src/error.rs index 6653c84..2193114 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,14 @@ use std::error; use std::fmt; +#[cfg(feature = "bb8_postgres")] +use bb8_postgres::tokio_postgres::Error as PostgresError; +#[cfg(all(feature = "r2d2_postgres", not(feature = "bb8_postgres")))] +use r2d2_postgres::postgres::Error as PostgresError; + +#[cfg(feature = "r2d2_sqlite")] +use r2d2_sqlite::rusqlite::Error as RusqliteError; + /// A helper type for any result with persistence error. /// /// Use this type in your repository or in something else that implements methods for your persistence. @@ -45,16 +53,16 @@ impl fmt::Display for PersistenceError { impl error::Error for PersistenceError {} -#[cfg(feature = "bb8_postgres")] -impl From for PersistenceError { - fn from(err: bb8_postgres::tokio_postgres::Error) -> Self { +#[cfg(any(feature = "r2d2_postgres", feature = "bb8_postgres"))] +impl From for PersistenceError { + fn from(err: PostgresError) -> Self { Self::DbError(Box::new(err)) } } -#[cfg(all(feature = "r2d2_postgres", not(feature = "bb8_postgres")))] -impl From for PersistenceError { - fn from(err: r2d2_postgres::postgres::Error) -> Self { +#[cfg(feature = "r2d2_sqlite")] +impl From for PersistenceError { + fn from(err: RusqliteError) -> Self { Self::DbError(Box::new(err)) } } diff --git a/src/lib.rs b/src/lib.rs index 7e9561b..52158ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,7 +57,7 @@ pub use bb8; /// This module contains implementation for async interface of postgres database. /// -/// The implementation uses bb8 as the pool and tokio_postgres as the client. +/// The implementation uses `bb8` as the pool and `tokio_postgres` as the client. /// /// **Note:** This mod requires enabling the `bb8_postgres` feature #[cfg(feature = "bb8_postgres")] @@ -70,12 +70,20 @@ pub use r2d2; /// This module contains implementation for sync interface of postgres database. /// -/// The implementation uses r2d2 as the pool and postgres as the client. +/// The implementation uses `r2d2` as the pool and `postgres` as the client. /// /// **Note:** This mod requires enabling the `r2d2_postgres` feature. #[cfg(feature = "r2d2_postgres")] pub mod r2d2_postgres; +/// This module contains implementation for sync interface of sqlite database. +/// +/// The implementation uses `r2d2` as the pool and `rusqlite` as the client. +/// +/// **Note:** This mod requires enabling the `r2d2_sqlite` feature. +#[cfg(feature = "r2d2_sqlite")] +pub mod r2d2_sqlite; + /// This module contains implementations for errors and result, that this /// crate uses pub mod error; diff --git a/src/r2d2_postgres.rs b/src/r2d2_postgres.rs index a0a76e5..7a36f48 100644 --- a/src/r2d2_postgres.rs +++ b/src/r2d2_postgres.rs @@ -9,7 +9,7 @@ pub use r2d2_postgres::PostgresConnectionManager as Manager; /// Inner connection of r2d2 implementation. pub type InnerConn = PooledConnection; -/// Inner connection of postgres connection. +/// Inner transaction of postgres. pub type InnerTrx<'t> = postgres::Transaction<'t>; /// Alias for r2d2 postgres no tls manager. @@ -37,7 +37,7 @@ pub struct Persistence<'p, M>(&'p Pool) where M: r2d2::ManageConnection; -impl<'p> PersistencePool for NoTlsPersistence<'p> { +impl PersistencePool for NoTlsPersistence<'_> { type Conn = NoTlsConnection; fn get_connection(&self) -> error::Result { diff --git a/src/r2d2_sqlite.rs b/src/r2d2_sqlite.rs new file mode 100644 index 0000000..821a055 --- /dev/null +++ b/src/r2d2_sqlite.rs @@ -0,0 +1,100 @@ +use crate::error; +#[cfg(feature = "nightly")] +use crate::syn::TransactionClient; +use crate::syn::{ConnectionClient, PersistencePool}; +pub use r2d2::{Pool, PooledConnection}; +pub use r2d2_sqlite::rusqlite; +pub use r2d2_sqlite::SqliteConnectionManager as Manager; + +/// Inner connection of r2d2 implementation. +pub type InnerConn = PooledConnection; +/// Inner transaction of rusqlite. +pub type InnerTrx<'t> = rusqlite::Transaction<'t>; + +/// It creates new persistence of r2d2 sqlite implementation. +pub fn new(pool: &Pool) -> Persistence { + Persistence(pool) +} + +/// Persistence wrap over r2d2 pool. +#[derive(Clone)] +pub struct Persistence<'p>(&'p Pool); + +impl PersistencePool for Persistence<'_> { + type Conn = Connection; + + fn get_connection(&self) -> error::Result { + self.0 + .get() + .map_err(|_| error::PersistenceError::GetConnection) + .map(Connection) + } +} + +/// Connection wrap over r2d2 sqlite inner connection. +pub struct Connection(InnerConn); + +impl ConnectionClient for Connection { + type InnerConn = InnerConn; + + #[cfg(feature = "nightly")] + type Trx<'t> = Transaction<'t>; + + fn inner(&mut self) -> &mut Self::InnerConn { + &mut self.0 + } + + #[cfg(feature = "nightly")] + fn start_transaction(&mut self) -> error::Result> { + self.0 + .transaction() + .map_err(|_| error::PersistenceError::UpgradeToTransaction) + .map(Transaction) + } +} + +/// Transaction wrap over rusqlite transaction. +/// +/// **Note:** requires nightly rust channel and enabling the `nightly` feature. +/// +/// # Limits +/// +/// It doesn't support nested transaction, because the transaction in `rusqlite` +/// requires DerefMut, which cannot be implemented at the moment. 😣 +pub struct Transaction<'me>(InnerTrx<'me>); + +impl<'me> ConnectionClient for Transaction<'me> { + type InnerConn = InnerTrx<'me>; + + #[cfg(feature = "nightly")] + type Trx<'t> = Transaction<'t>; + + fn inner(&mut self) -> &mut Self::InnerConn { + &mut self.0 + } + + #[cfg(feature = "nightly")] + fn start_transaction(&mut self) -> error::Result> { + // At the moment we cannot implement nested transaction because + // the transaction in `rusqlite` requires DerefMut, which cannot be + // implemented yet 😣 + unimplemented!() + // self.0 + // .transaction() + // .map_err(|_| error::PersistenceError::UpgradeToTransaction) + // .map(Transaction) + } +} + +impl TransactionClient for Transaction<'_> { + fn commit(self) -> error::Result<()> { + self.0 + .commit() + .map_err(|_| error::PersistenceError::CommitTransaction) + } + fn rollback(self) -> error::Result<()> { + self.0 + .rollback() + .map_err(|_| error::PersistenceError::RollbackTransaction) + } +}