Mock Database Connection
This commit is contained in:
parent
da545c266c
commit
6b7ea75393
12
Cargo.toml
12
Cargo.toml
@ -23,8 +23,9 @@ name = "sea_orm"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "^0.1"
|
async-stream = { version = "^0.3" }
|
||||||
futures = { version = "^0.3" }
|
futures = { version = "^0.3" }
|
||||||
|
futures-util = { version = "^0.3" }
|
||||||
sea-query = { path = "../sea-query", version = "^0.12" }
|
sea-query = { path = "../sea-query", version = "^0.12" }
|
||||||
sea-orm-macros = { path = "sea-orm-macros", optional = true }
|
sea-orm-macros = { path = "sea-orm-macros", optional = true }
|
||||||
# sea-schema = { path = "../sea-schema" }
|
# sea-schema = { path = "../sea-schema" }
|
||||||
@ -32,15 +33,14 @@ serde = { version = "^1.0", features = [ "derive" ] }
|
|||||||
sqlx = { version = "^0.5", optional = true }
|
sqlx = { version = "^0.5", optional = true }
|
||||||
strum = { version = "^0.20", features = [ "derive" ] }
|
strum = { version = "^0.20", features = [ "derive" ] }
|
||||||
serde_json = { version = "^1", optional = true }
|
serde_json = { version = "^1", optional = true }
|
||||||
async-stream = { version = "^0.3" }
|
|
||||||
futures-util = { version = "^0.3" }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
debug-print = []
|
debug-print = []
|
||||||
default = [ "macros", "sqlx-mysql", "runtime-async-std-native-tls", "with-json" ]
|
default = [ "macros", "with-json", "mock", "sqlx-mysql", "runtime-async-std-native-tls" ]
|
||||||
macros = [ "sea-orm-macros" ]
|
macros = [ "sea-orm-macros" ]
|
||||||
with-json = [ "serde_json" ]
|
mock = []
|
||||||
sqlx-dep = [ "sqlx" ]
|
with-json = [ "serde_json", "sea-query/with-json" ]
|
||||||
|
sqlx-dep = [ "sqlx", "sqlx/json" ]
|
||||||
sqlx-mysql = [ "sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql" ]
|
sqlx-mysql = [ "sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql" ]
|
||||||
sqlx-postgres = [ "sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres" ]
|
sqlx-postgres = [ "sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres" ]
|
||||||
runtime-actix-native-tls = [ "sqlx/runtime-actix-native-tls" ]
|
runtime-actix-native-tls = [ "sqlx/runtime-actix-native-tls" ]
|
||||||
|
@ -6,7 +6,7 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-std = { version = "^1.9", features = [ "attributes" ] }
|
async-std = { version = "^1.9", features = [ "attributes" ] }
|
||||||
sea-orm = { path = "../../", features = [ "sqlx-mysql", "runtime-async-std-native-tls", "debug-print" ] }
|
sea-orm = { path = "../../", features = [ "sqlx-mysql", "runtime-async-std-native-tls", "debug-print", "with-json", "macros" ], default-features = false }
|
||||||
# sea-query = { path = "../../../sea-query" }
|
# sea-query = { path = "../../../sea-query" }
|
||||||
strum = { version = "^0.20", features = [ "derive" ] }
|
strum = { version = "^0.20", features = [ "derive" ] }
|
||||||
serde_json = { version = "^1" }
|
serde_json = { version = "^1" }
|
||||||
|
137
src/database/connection.rs
Normal file
137
src/database/connection.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use crate::{ExecErr, ExecResult, QueryErr, QueryResult, Statement};
|
||||||
|
use sea_query::{
|
||||||
|
DeleteStatement, InsertStatement, MysqlQueryBuilder, PostgresQueryBuilder, SelectStatement,
|
||||||
|
UpdateStatement,
|
||||||
|
};
|
||||||
|
use std::{error::Error, fmt};
|
||||||
|
|
||||||
|
pub enum DatabaseConnection {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
|
SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection),
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
MockDatabaseConnection(crate::MockDatabaseConnection),
|
||||||
|
Disconnected,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum QueryBuilderBackend {
|
||||||
|
MySql,
|
||||||
|
Postgres,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ConnectionErr;
|
||||||
|
|
||||||
|
impl Error for ConnectionErr {}
|
||||||
|
|
||||||
|
impl fmt::Display for ConnectionErr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DatabaseConnection {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Disconnected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for DatabaseConnection {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
|
Self::SqlxMySqlPoolConnection(_) => "SqlxMySqlPoolConnection",
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
Self::MockDatabaseConnection(_) => "MockDatabaseConnection",
|
||||||
|
Self::Disconnected => "Disconnected",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DatabaseConnection {
|
||||||
|
pub fn get_query_builder_backend(&self) -> QueryBuilderBackend {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
|
DatabaseConnection::SqlxMySqlPoolConnection(_) => QueryBuilderBackend::MySql,
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
DatabaseConnection::MockDatabaseConnection(_) => QueryBuilderBackend::Postgres,
|
||||||
|
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
stmt: Statement,
|
||||||
|
) -> Result<ExecResult, ExecErr> {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
|
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.execute(stmt).await,
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt).await,
|
||||||
|
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_one(
|
||||||
|
&self,
|
||||||
|
stmt: Statement,
|
||||||
|
) -> Result<Option<QueryResult>, QueryErr> {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
|
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_one(stmt).await,
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt).await,
|
||||||
|
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_all(
|
||||||
|
&self,
|
||||||
|
stmt: Statement,
|
||||||
|
) -> Result<Vec<QueryResult>, QueryErr> {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
|
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_all(stmt).await,
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt).await,
|
||||||
|
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueryBuilderBackend {
|
||||||
|
pub fn build_select_statement(&self, statement: &SelectStatement) -> Statement {
|
||||||
|
match self {
|
||||||
|
Self::MySql => statement.build(MysqlQueryBuilder),
|
||||||
|
Self::Postgres => statement.build(PostgresQueryBuilder),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_insert_statement(&self, statement: &InsertStatement) -> Statement {
|
||||||
|
match self {
|
||||||
|
Self::MySql => statement.build(MysqlQueryBuilder),
|
||||||
|
Self::Postgres => statement.build(PostgresQueryBuilder),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_update_statement(&self, statement: &UpdateStatement) -> Statement {
|
||||||
|
match self {
|
||||||
|
Self::MySql => statement.build(MysqlQueryBuilder),
|
||||||
|
Self::Postgres => statement.build(PostgresQueryBuilder),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_delete_statement(&self, statement: &DeleteStatement) -> Statement {
|
||||||
|
match self {
|
||||||
|
Self::MySql => statement.build(MysqlQueryBuilder),
|
||||||
|
Self::Postgres => statement.build(PostgresQueryBuilder),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
66
src/database/mock.rs
Normal file
66
src/database/mock.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use crate::{ExecErr, ExecResult, MockDatabaseTrait, QueryErr, QueryResult, Statement, TypeErr};
|
||||||
|
use sea_query::{Value, ValueType};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct MockDatabase {
|
||||||
|
transaction_log: Vec<Statement>,
|
||||||
|
exec_results: Vec<ExecResult>,
|
||||||
|
query_results: Vec<Vec<QueryResult>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MockExecResult {
|
||||||
|
pub last_insert_id: u64,
|
||||||
|
pub rows_affected: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MockRow {
|
||||||
|
values: BTreeMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockDatabase {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockDatabaseTrait for MockDatabase {
|
||||||
|
fn execute(&mut self, counter: usize, statement: Statement) -> Result<ExecResult, ExecErr> {
|
||||||
|
self.transaction_log.push(statement);
|
||||||
|
if counter < self.exec_results.len() {
|
||||||
|
Err(ExecErr)
|
||||||
|
// Ok(self.exec_results[counter].clone())
|
||||||
|
} else {
|
||||||
|
Err(ExecErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query(
|
||||||
|
&mut self,
|
||||||
|
counter: usize,
|
||||||
|
statement: Statement,
|
||||||
|
) -> Result<Vec<QueryResult>, QueryErr> {
|
||||||
|
self.transaction_log.push(statement);
|
||||||
|
if counter < self.query_results.len() {
|
||||||
|
Err(QueryErr)
|
||||||
|
// Ok(self.query_results[counter].clone())
|
||||||
|
} else {
|
||||||
|
Err(QueryErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockRow {
|
||||||
|
pub fn try_get<T>(&self, col: &str) -> Result<T, TypeErr>
|
||||||
|
where
|
||||||
|
T: ValueType,
|
||||||
|
{
|
||||||
|
Ok(self.values.get(col).unwrap().clone().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_column_value_tuples(self) -> impl Iterator<Item = (String, Value)> {
|
||||||
|
self.values.into_iter()
|
||||||
|
}
|
||||||
|
}
|
@ -1,63 +1,38 @@
|
|||||||
|
mod connection;
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
mod mock;
|
||||||
mod statement;
|
mod statement;
|
||||||
|
|
||||||
|
pub use connection::*;
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
pub use mock::*;
|
||||||
pub use statement::*;
|
pub use statement::*;
|
||||||
|
|
||||||
use crate::{Connection, ConnectionErr, Connector, SqlxMySqlConnector, SqlxMySqlPoolConnection};
|
|
||||||
use sea_query::{GenericBuilder, MySqlQueryBuilder};
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
connection: DatabaseConnection,
|
connection: DatabaseConnection,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum DatabaseConnection {
|
|
||||||
SqlxMySqlPoolConnection(SqlxMySqlPoolConnection),
|
|
||||||
Disconnected,
|
|
||||||
}
|
|
||||||
|
|
||||||
// DatabaseConnection //
|
|
||||||
|
|
||||||
impl Default for DatabaseConnection {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Disconnected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for DatabaseConnection {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
match self {
|
|
||||||
Self::SqlxMySqlPoolConnection(_) => "SqlxMySqlPoolConnection",
|
|
||||||
Self::Disconnected => "Disconnected",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Database //
|
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
pub async fn connect(&mut self, string: &str) -> Result<(), ConnectionErr> {
|
pub async fn connect(&mut self, string: &str) -> Result<(), ConnectionErr> {
|
||||||
if SqlxMySqlConnector::accepts(string) {
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
self.connection = SqlxMySqlConnector::connect(string).await?;
|
if crate::SqlxMySqlConnector::accepts(string) {
|
||||||
|
self.connection = crate::SqlxMySqlConnector::connect(string).await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
if crate::MockDatabaseConnector::accepts(string) {
|
||||||
|
self.connection = crate::MockDatabaseConnector::connect(string).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Err(ConnectionErr)
|
Err(ConnectionErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_connection(&self) -> impl Connection + '_ {
|
pub fn get_connection(&self) -> &DatabaseConnection {
|
||||||
match &self.connection {
|
&self.connection
|
||||||
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn,
|
|
||||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_query_builder_backend(&self) -> impl GenericBuilder {
|
pub fn get_query_builder_backend(&self) -> QueryBuilderBackend {
|
||||||
match &self.connection {
|
self.connection.get_query_builder_backend()
|
||||||
DatabaseConnection::SqlxMySqlPoolConnection(_) => MySqlQueryBuilder::default(),
|
|
||||||
DatabaseConnection::Disconnected => panic!("Disconnected"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use sea_query::{inject_parameters, MySqlQueryBuilder, Values};
|
use sea_query::{inject_parameters, MySqlQueryBuilder, Values};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Statement {
|
pub struct Statement {
|
||||||
pub sql: String,
|
pub sql: String,
|
||||||
pub values: Option<Values>,
|
pub values: Option<Values>,
|
||||||
|
76
src/driver/mock.rs
Normal file
76
src/driver/mock.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use crate::{
|
||||||
|
debug_print, ConnectionErr, DatabaseConnection, ExecErr, ExecResult, MockDatabase, QueryErr,
|
||||||
|
QueryResult, Statement,
|
||||||
|
};
|
||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct MockDatabaseConnector;
|
||||||
|
|
||||||
|
pub struct MockDatabaseConnection {
|
||||||
|
counter: AtomicUsize,
|
||||||
|
mocker: Mutex<Box<dyn MockDatabaseTrait>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MockDatabaseTrait: Send {
|
||||||
|
fn execute(&mut self, counter: usize, stmt: Statement) -> Result<ExecResult, ExecErr>;
|
||||||
|
|
||||||
|
fn query(&mut self, counter: usize, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockDatabaseConnector {
|
||||||
|
pub fn accepts(_string: &str) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn connect(_string: &str) -> Result<DatabaseConnection, ConnectionErr> {
|
||||||
|
Ok(DatabaseConnection::MockDatabaseConnection(
|
||||||
|
MockDatabaseConnection::new(MockDatabase::new()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockDatabaseConnection {
|
||||||
|
pub fn new<M: 'static>(m: M) -> Self
|
||||||
|
where
|
||||||
|
M: MockDatabaseTrait,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
counter: AtomicUsize::new(0),
|
||||||
|
mocker: Mutex::new(Box::new(m)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockDatabaseConnection {
|
||||||
|
pub async fn execute(&self, statement: Statement) -> Result<ExecResult, ExecErr> {
|
||||||
|
debug_print!("{}", statement);
|
||||||
|
self.counter.fetch_add(1, Ordering::SeqCst);
|
||||||
|
self.mocker
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.execute(self.counter.load(Ordering::SeqCst), statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, QueryErr> {
|
||||||
|
debug_print!("{}", statement);
|
||||||
|
self.counter.fetch_add(1, Ordering::SeqCst);
|
||||||
|
let result = self
|
||||||
|
.mocker
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.query(self.counter.load(Ordering::SeqCst), statement)?;
|
||||||
|
Ok(result.into_iter().next())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_all(&self, statement: Statement) -> Result<Vec<QueryResult>, QueryErr> {
|
||||||
|
debug_print!("{}", statement);
|
||||||
|
self.counter.fetch_add(1, Ordering::SeqCst);
|
||||||
|
self.mocker
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.query(self.counter.load(Ordering::SeqCst), statement)
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,9 @@
|
|||||||
|
#[cfg(feature = "mock")]
|
||||||
|
mod mock;
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
mod sqlx_mysql;
|
mod sqlx_mysql;
|
||||||
|
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
pub use mock::*;
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
pub use sqlx_mysql::*;
|
pub use sqlx_mysql::*;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use async_trait::async_trait;
|
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
mysql::{MySqlArguments, MySqlQueryResult, MySqlRow},
|
mysql::{MySqlArguments, MySqlQueryResult, MySqlRow},
|
||||||
MySql, MySqlPool,
|
MySql, MySqlPool,
|
||||||
@ -7,7 +6,7 @@ use sqlx::{
|
|||||||
sea_query::sea_query_driver_mysql!();
|
sea_query::sea_query_driver_mysql!();
|
||||||
use sea_query_driver_mysql::bind_query;
|
use sea_query_driver_mysql::bind_query;
|
||||||
|
|
||||||
use crate::{debug_print, executor::*, DatabaseConnection, Statement};
|
use crate::{debug_print, executor::*, DatabaseConnection, Statement, ConnectionErr};
|
||||||
|
|
||||||
pub struct SqlxMySqlConnector;
|
pub struct SqlxMySqlConnector;
|
||||||
|
|
||||||
@ -15,13 +14,12 @@ pub struct SqlxMySqlPoolConnection {
|
|||||||
pool: MySqlPool,
|
pool: MySqlPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
impl SqlxMySqlConnector {
|
||||||
impl Connector for SqlxMySqlConnector {
|
pub fn accepts(string: &str) -> bool {
|
||||||
fn accepts(string: &str) -> bool {
|
|
||||||
string.starts_with("mysql://")
|
string.starts_with("mysql://")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn connect(string: &str) -> Result<DatabaseConnection, ConnectionErr> {
|
pub async fn connect(string: &str) -> Result<DatabaseConnection, ConnectionErr> {
|
||||||
if let Ok(pool) = MySqlPool::connect(string).await {
|
if let Ok(pool) = MySqlPool::connect(string).await {
|
||||||
Ok(DatabaseConnection::SqlxMySqlPoolConnection(
|
Ok(DatabaseConnection::SqlxMySqlPoolConnection(
|
||||||
SqlxMySqlPoolConnection { pool },
|
SqlxMySqlPoolConnection { pool },
|
||||||
@ -38,9 +36,8 @@ impl SqlxMySqlConnector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
impl SqlxMySqlPoolConnection {
|
||||||
impl Connection for &SqlxMySqlPoolConnection {
|
pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr> {
|
||||||
async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr> {
|
|
||||||
debug_print!("{}", stmt);
|
debug_print!("{}", stmt);
|
||||||
|
|
||||||
let query = sqlx_query(&stmt);
|
let query = sqlx_query(&stmt);
|
||||||
@ -52,7 +49,7 @@ impl Connection for &SqlxMySqlPoolConnection {
|
|||||||
Err(ExecErr)
|
Err(ExecErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, QueryErr> {
|
pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, QueryErr> {
|
||||||
debug_print!("{}", stmt);
|
debug_print!("{}", stmt);
|
||||||
|
|
||||||
let query = sqlx_query(&stmt);
|
let query = sqlx_query(&stmt);
|
||||||
@ -67,7 +64,7 @@ impl Connection for &SqlxMySqlPoolConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr> {
|
pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr> {
|
||||||
debug_print!("{}", stmt);
|
debug_print!("{}", stmt);
|
||||||
|
|
||||||
let query = sqlx_query(&stmt);
|
let query = sqlx_query(&stmt);
|
||||||
@ -96,6 +93,18 @@ impl From<MySqlQueryResult> for ExecResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<sqlx::Error> for TypeErr {
|
||||||
|
fn from(_: sqlx::Error) -> TypeErr {
|
||||||
|
TypeErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sqlx::Error> for ExecErr {
|
||||||
|
fn from(_: sqlx::Error) -> ExecErr {
|
||||||
|
ExecErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, MySql, MySqlArguments> {
|
fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, MySql, MySqlArguments> {
|
||||||
let mut query = sqlx::query(&stmt.sql);
|
let mut query = sqlx::query(&stmt.sql);
|
||||||
if let Some(values) = &stmt.values {
|
if let Some(values) = &stmt.values {
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
use super::{ExecErr, ExecResult, QueryErr, QueryResult};
|
|
||||||
use crate::{DatabaseConnection, Statement};
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::{error::Error, fmt};
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait Connector {
|
|
||||||
fn accepts(string: &str) -> bool;
|
|
||||||
|
|
||||||
async fn connect(string: &str) -> Result<DatabaseConnection, ConnectionErr>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait Connection {
|
|
||||||
async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr>;
|
|
||||||
|
|
||||||
async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, QueryErr>;
|
|
||||||
|
|
||||||
async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ConnectionErr;
|
|
||||||
|
|
||||||
// ConnectionErr //
|
|
||||||
|
|
||||||
impl Error for ConnectionErr {}
|
|
||||||
|
|
||||||
impl fmt::Display for ConnectionErr {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{:?}", self)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ActiveModelTrait, Connection, Database, DeleteMany, DeleteOne, EntityTrait, ExecErr, Statement,
|
ActiveModelTrait, Database, DeleteMany, DeleteOne, EntityTrait, ExecErr, QueryBuilderBackend,
|
||||||
|
Statement,
|
||||||
};
|
};
|
||||||
use sea_query::{DeleteStatement, QueryBuilder};
|
use sea_query::DeleteStatement;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -45,11 +46,8 @@ impl Deleter {
|
|||||||
Self { query }
|
Self { query }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build<B>(&self, builder: B) -> Statement
|
pub fn build(&self, builder: QueryBuilderBackend) -> Statement {
|
||||||
where
|
builder.build_delete_statement(&self.query)
|
||||||
B: QueryBuilder,
|
|
||||||
{
|
|
||||||
self.query.build(builder).into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec(self, db: &Database) -> impl Future<Output = Result<DeleteResult, ExecErr>> + '_ {
|
pub fn exec(self, db: &Database) -> impl Future<Output = Result<DeleteResult, ExecErr>> + '_ {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use sqlx::mysql::MySqlQueryResult;
|
|
||||||
use std::{error::Error, fmt};
|
use std::{error::Error, fmt};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -8,7 +7,10 @@ pub struct ExecResult {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum ExecResultHolder {
|
pub(crate) enum ExecResultHolder {
|
||||||
SqlxMySql(MySqlQueryResult),
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
|
SqlxMySql(sqlx::mysql::MySqlQueryResult),
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
Mock(crate::MockExecResult),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -19,13 +21,19 @@ pub struct ExecErr;
|
|||||||
impl ExecResult {
|
impl ExecResult {
|
||||||
pub fn last_insert_id(&self) -> u64 {
|
pub fn last_insert_id(&self) -> u64 {
|
||||||
match &self.result {
|
match &self.result {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
ExecResultHolder::SqlxMySql(result) => result.last_insert_id(),
|
ExecResultHolder::SqlxMySql(result) => result.last_insert_id(),
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
ExecResultHolder::Mock(result) => result.last_insert_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rows_affected(&self) -> u64 {
|
pub fn rows_affected(&self) -> u64 {
|
||||||
match &self.result {
|
match &self.result {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
ExecResultHolder::SqlxMySql(result) => result.rows_affected(),
|
ExecResultHolder::SqlxMySql(result) => result.rows_affected(),
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
ExecResultHolder::Mock(result) => result.rows_affected,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,9 +47,3 @@ impl fmt::Display for ExecErr {
|
|||||||
write!(f, "{:?}", self)
|
write!(f, "{:?}", self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<sqlx::Error> for ExecErr {
|
|
||||||
fn from(_: sqlx::Error) -> ExecErr {
|
|
||||||
ExecErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::{ActiveModelTrait, Connection, Database, ExecErr, Insert, QueryTrait, Statement};
|
use crate::{
|
||||||
use sea_query::{InsertStatement, QueryBuilder};
|
ActiveModelTrait, Database, ExecErr, Insert, QueryBuilderBackend, QueryTrait, Statement,
|
||||||
|
};
|
||||||
|
use sea_query::InsertStatement;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -27,11 +29,8 @@ impl Inserter {
|
|||||||
Self { query }
|
Self { query }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build<B>(&self, builder: B) -> Statement
|
pub fn build(&self, builder: QueryBuilderBackend) -> Statement {
|
||||||
where
|
builder.build_insert_statement(&self.query)
|
||||||
B: QueryBuilder,
|
|
||||||
{
|
|
||||||
self.query.build(builder).into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec(self, db: &Database) -> impl Future<Output = Result<InsertResult, ExecErr>> + '_ {
|
pub fn exec(self, db: &Database) -> impl Future<Output = Result<InsertResult, ExecErr>> + '_ {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
mod connector;
|
|
||||||
mod delete;
|
mod delete;
|
||||||
mod execute;
|
mod execute;
|
||||||
mod insert;
|
mod insert;
|
||||||
@ -7,7 +6,6 @@ mod query;
|
|||||||
mod select;
|
mod select;
|
||||||
mod update;
|
mod update;
|
||||||
|
|
||||||
pub use connector::*;
|
|
||||||
pub use delete::*;
|
pub use delete::*;
|
||||||
pub use execute::*;
|
pub use execute::*;
|
||||||
pub use insert::*;
|
pub use insert::*;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{Connection, Database, QueryErr, SelectorTrait};
|
use crate::{Database, QueryErr, SelectorTrait};
|
||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use sea_query::{Alias, Expr, SelectStatement};
|
use sea_query::{Alias, Expr, SelectStatement};
|
||||||
@ -31,7 +31,7 @@ where
|
|||||||
.offset((self.page_size * page) as u64)
|
.offset((self.page_size * page) as u64)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
let builder = self.db.get_query_builder_backend();
|
let builder = self.db.get_query_builder_backend();
|
||||||
let stmt = query.build(builder).into();
|
let stmt = builder.build_select_statement(&query);
|
||||||
let rows = self.db.get_connection().query_all(stmt).await?;
|
let rows = self.db.get_connection().query_all(stmt).await?;
|
||||||
let mut buffer = Vec::with_capacity(rows.len());
|
let mut buffer = Vec::with_capacity(rows.len());
|
||||||
for row in rows.into_iter() {
|
for row in rows.into_iter() {
|
||||||
@ -49,14 +49,14 @@ where
|
|||||||
/// Get the total number of pages
|
/// Get the total number of pages
|
||||||
pub async fn num_pages(&self) -> Result<usize, QueryErr> {
|
pub async fn num_pages(&self) -> Result<usize, QueryErr> {
|
||||||
let builder = self.db.get_query_builder_backend();
|
let builder = self.db.get_query_builder_backend();
|
||||||
let stmt = SelectStatement::new()
|
let stmt = builder.build_select_statement(
|
||||||
|
SelectStatement::new()
|
||||||
.expr(Expr::cust("COUNT(*) AS num_rows"))
|
.expr(Expr::cust("COUNT(*) AS num_rows"))
|
||||||
.from_subquery(
|
.from_subquery(
|
||||||
self.query.clone().reset_limit().reset_offset().to_owned(),
|
self.query.clone().reset_limit().reset_offset().to_owned(),
|
||||||
Alias::new("sub_query"),
|
Alias::new("sub_query"),
|
||||||
)
|
),
|
||||||
.build(builder)
|
);
|
||||||
.into();
|
|
||||||
let result = match self.db.get_connection().query_one(stmt).await? {
|
let result = match self.db.get_connection().query_one(stmt).await? {
|
||||||
Some(res) => res,
|
Some(res) => res,
|
||||||
None => return Ok(0),
|
None => return Ok(0),
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use sqlx::mysql::MySqlRow;
|
|
||||||
use std::{error::Error, fmt};
|
use std::{error::Error, fmt};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -8,7 +7,10 @@ pub struct QueryResult {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum QueryResultRow {
|
pub(crate) enum QueryResultRow {
|
||||||
SqlxMySql(MySqlRow),
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
|
SqlxMySql(sqlx::mysql::MySqlRow),
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
Mock(crate::MockRow),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -60,12 +62,6 @@ impl fmt::Display for TypeErr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<sqlx::Error> for TypeErr {
|
|
||||||
fn from(_: sqlx::Error) -> TypeErr {
|
|
||||||
TypeErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TryGetable //
|
// TryGetable //
|
||||||
|
|
||||||
macro_rules! try_getable {
|
macro_rules! try_getable {
|
||||||
@ -74,10 +70,13 @@ macro_rules! try_getable {
|
|||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> {
|
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> {
|
||||||
let column = format!("{}{}", pre, col);
|
let column = format!("{}{}", pre, col);
|
||||||
match &res.row {
|
match &res.row {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
QueryResultRow::SqlxMySql(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
Ok(row.try_get(column.as_str())?)
|
Ok(row.try_get(column.as_str())?)
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,6 +85,7 @@ macro_rules! try_getable {
|
|||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> {
|
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> {
|
||||||
let column = format!("{}{}", pre, col);
|
let column = format!("{}{}", pre, col);
|
||||||
match &res.row {
|
match &res.row {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
QueryResultRow::SqlxMySql(row) => {
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
match row.try_get(column.as_str()) {
|
match row.try_get(column.as_str()) {
|
||||||
@ -93,6 +93,11 @@ macro_rules! try_getable {
|
|||||||
Err(_) => Ok(None),
|
Err(_) => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
QueryResultRow::Mock(row) => match row.try_get(column.as_str()) {
|
||||||
|
Ok(v) => Ok(Some(v)),
|
||||||
|
Err(_) => Ok(None),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
query::combine, Connection, Database, EntityTrait, FromQueryResult, JsonValue, Paginator,
|
query::combine, Database, EntityTrait, FromQueryResult, JsonValue, Paginator,
|
||||||
QueryErr, QueryResult, Select, SelectTwo, Statement, TypeErr,
|
QueryBuilderBackend, QueryErr, QueryResult, Select, SelectTwo, Statement, TypeErr,
|
||||||
};
|
};
|
||||||
use sea_query::{QueryBuilder, SelectStatement};
|
use sea_query::SelectStatement;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -134,11 +134,8 @@ impl<S> Selector<S>
|
|||||||
where
|
where
|
||||||
S: SelectorTrait,
|
S: SelectorTrait,
|
||||||
{
|
{
|
||||||
pub fn build<B>(&self, builder: B) -> Statement
|
pub fn build(&self, builder: QueryBuilderBackend) -> Statement {
|
||||||
where
|
builder.build_select_statement(&self.query)
|
||||||
B: QueryBuilder,
|
|
||||||
{
|
|
||||||
self.query.build(builder).into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn one(mut self, db: &Database) -> Result<Option<S::Item>, QueryErr> {
|
pub async fn one(mut self, db: &Database) -> Result<Option<S::Item>, QueryErr> {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ActiveModelTrait, Connection, Database, EntityTrait, ExecErr, Statement, UpdateMany, UpdateOne,
|
ActiveModelTrait, Database, EntityTrait, ExecErr, QueryBuilderBackend, Statement, UpdateMany,
|
||||||
|
UpdateOne,
|
||||||
};
|
};
|
||||||
use sea_query::{QueryBuilder, UpdateStatement};
|
use sea_query::UpdateStatement;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -42,11 +43,8 @@ impl Updater {
|
|||||||
Self { query }
|
Self { query }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build<B>(&self, builder: B) -> Statement
|
pub fn build(&self, builder: QueryBuilderBackend) -> Statement {
|
||||||
where
|
builder.build_update_statement(&self.query)
|
||||||
B: QueryBuilder,
|
|
||||||
{
|
|
||||||
self.query.build(builder).into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec(self, db: &Database) -> impl Future<Output = Result<UpdateResult, ExecErr>> + '_ {
|
pub fn exec(self, db: &Database) -> impl Future<Output = Result<UpdateResult, ExecErr>> + '_ {
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
use crate::{FromQueryResult, QueryResult, QueryResultRow, TypeErr};
|
use crate::{FromQueryResult, QueryResult, QueryResultRow, TypeErr};
|
||||||
|
use serde_json::Map;
|
||||||
pub use serde_json::Value as JsonValue;
|
pub use serde_json::Value as JsonValue;
|
||||||
use serde_json::{json, Map};
|
|
||||||
|
|
||||||
impl FromQueryResult for JsonValue {
|
impl FromQueryResult for JsonValue {
|
||||||
fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||||
match &res.row {
|
match &res.row {
|
||||||
|
#[cfg(feature = "sqlx-mysql")]
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
QueryResultRow::SqlxMySql(row) => {
|
||||||
|
use serde_json::json;
|
||||||
use sqlx::{Column, MySql, Row, Type};
|
use sqlx::{Column, MySql, Row, Type};
|
||||||
let mut map = Map::new();
|
let mut map = Map::new();
|
||||||
for column in row.columns() {
|
for column in row.columns() {
|
||||||
@ -41,6 +43,19 @@ impl FromQueryResult for JsonValue {
|
|||||||
}
|
}
|
||||||
Ok(JsonValue::Object(map))
|
Ok(JsonValue::Object(map))
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
QueryResultRow::Mock(row) => {
|
||||||
|
let mut map = Map::new();
|
||||||
|
for (column, value) in row.clone().into_column_value_tuples() {
|
||||||
|
let col = if !column.starts_with(pre) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
column.replacen(pre, "", 1)
|
||||||
|
};
|
||||||
|
map.insert(col, sea_query::sea_value_to_json_value(&value));
|
||||||
|
}
|
||||||
|
Ok(JsonValue::Object(map))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
use sqlx::mysql::MySqlRow;
|
|
||||||
use std::{error::Error, fmt};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct QueryResult {
|
|
||||||
pub(crate) row: QueryResultRow,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) enum QueryResultRow {
|
|
||||||
SqlxMySql(MySqlRow),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TypeErr;
|
|
||||||
|
|
||||||
pub trait TryGetable {
|
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TryGetable //
|
|
||||||
|
|
||||||
macro_rules! try_getable {
|
|
||||||
( $type: ty ) => {
|
|
||||||
impl TryGetable for $type {
|
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> {
|
|
||||||
let column = format!("{}{}", pre, col);
|
|
||||||
match &res.row {
|
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
|
||||||
use sqlx::Row;
|
|
||||||
Ok(row.try_get(column.as_str())?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryGetable for Option<$type> {
|
|
||||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TypeErr> {
|
|
||||||
let column = format!("{}{}", pre, col);
|
|
||||||
match &res.row {
|
|
||||||
QueryResultRow::SqlxMySql(row) => {
|
|
||||||
use sqlx::Row;
|
|
||||||
match row.try_get(column.as_str()) {
|
|
||||||
Ok(v) => Ok(Some(v)),
|
|
||||||
Err(_) => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
try_getable!(bool);
|
|
||||||
try_getable!(i8);
|
|
||||||
try_getable!(i16);
|
|
||||||
try_getable!(i32);
|
|
||||||
try_getable!(i64);
|
|
||||||
try_getable!(u8);
|
|
||||||
try_getable!(u16);
|
|
||||||
try_getable!(u32);
|
|
||||||
try_getable!(u64);
|
|
||||||
try_getable!(f32);
|
|
||||||
try_getable!(f64);
|
|
||||||
try_getable!(String);
|
|
||||||
|
|
||||||
// QueryResult //
|
|
||||||
|
|
||||||
impl QueryResult {
|
|
||||||
pub fn try_get<T>(&self, pre: &str, col: &str) -> Result<T, TypeErr>
|
|
||||||
where
|
|
||||||
T: TryGetable,
|
|
||||||
{
|
|
||||||
T::try_get(self, pre, col)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TypeErr //
|
|
||||||
|
|
||||||
impl Error for TypeErr {}
|
|
||||||
|
|
||||||
impl fmt::Display for TypeErr {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{:?}", self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<sqlx::Error> for TypeErr {
|
|
||||||
fn from(_: sqlx::Error) -> TypeErr {
|
|
||||||
TypeErr
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user