use anyhow::{Context, Result as AnyhowResult};
use async_trait::async_trait;
use sqlx::{query::Query, Pool};
#[cfg(feature = "mysql")]
use sqlx::{mysql::MySqlArguments, MySql as TinyOrmDatabase};
#[cfg(feature = "mysql")]
pub type TinyOrmDbPool = Pool<TinyOrmDatabase>;
#[cfg(feature = "mysql")]
pub type TinyOrmSqlRow = <TinyOrmDatabase as sqlx::Database>::Row;
#[cfg(feature = "mysql")]
pub type TinyOrmSqlResult = <TinyOrmDatabase as sqlx::Database>::QueryResult;
#[cfg(feature = "postgres")]
use sqlx::{postgres::PgArguments, Postgres as TinyOrmDatabase};
#[cfg(feature = "postgres")]
pub type TinyOrmDbPool = Pool<TinyOrmDatabase>;
#[cfg(feature = "postgres")]
pub type TinyOrmSqlRow = <TinyOrmDatabase as sqlx::Database>::Row;
#[cfg(feature = "postgres")]
pub type TinyOrmSqlResult = <TinyOrmDatabase as sqlx::Database>::QueryResult;
#[cfg(feature = "sqlite")]
use sqlx::{sqlite::SqliteArguments, Sqlite as TinyOrmDatabase};
#[cfg(feature = "sqlite")]
pub type TinyOrmDbPool = Pool<TinyOrmDatabase>;
#[cfg(feature = "sqlite")]
pub type TinyOrmSqlRow = <TinyOrmDatabase as sqlx::Database>::Row;
#[cfg(feature = "sqlite")]
pub type TinyOrmSqlResult = <TinyOrmDatabase as sqlx::Database>::QueryResult;
#[cfg(feature = "any")]
use sqlx::{any::AnyArguments, Any as TinyOrmDatabase};
#[cfg(feature = "any")]
pub type TinyOrmDbPool = Pool<TinyOrmDatabase>;
#[cfg(feature = "any")]
pub type TinyOrmSqlRow = <TinyOrmDatabase as sqlx::Database>::Row;
#[cfg(feature = "any")]
pub type TinyOrmSqlResult = <TinyOrmDatabase as sqlx::Database>::QueryResult;
#[derive(Debug)]
pub struct TinyOrmDbMeta {
pub table_name: &'static str,
pub select_sql: &'static str,
pub update_sql: &'static str,
pub update_set_sql: &'static str,
pub pk_where_sql: &'static str,
}
impl TinyOrmDbMeta {
pub fn build_select_sql(&self, where_sql: &str) -> String {
self.build_select_sql_with_order(where_sql, None)
}
pub fn build_select_sql_with_order(&self, where_sql: &str,order_by:Option<&str>) -> String {
if let Some(order_by) = order_by {
format!("{} WHERE {} ORDER BY {}", self.select_sql, where_sql,order_by)
}else{
format!("{} WHERE {} ", self.select_sql, where_sql)
}
}
pub fn build_exist_sql(&self, where_sql: &str) -> String {
format!(
"SELECT count(1) FROM {} WHERE {}",
self.table_name, where_sql
)
}
pub fn build_insert_sql(&self, insert_field_value: &str) -> String {
format!("INSERT INTO {} {}", self.table_name, insert_field_value)
}
pub fn build_delete_sql(&self, delete_where_sql: &str) -> String {
format!(
"DELETE FROM {} WHERE {}",
self.table_name, delete_where_sql
)
}
pub fn build_update_sql(&self, set_sql: &str, where_sql: Option<&str>) -> String {
if let Some(where_sql) = where_sql {
format!(
"UPDATE {} SET {} WHERE {}",
self.table_name, set_sql, where_sql
)
} else {
format!("UPDATE {} SET {}", self.table_name, set_sql)
}
}
pub fn build_update_sql_with_pk(&self, set_sql: &str) -> String {
self.build_update_sql(set_sql, Some(self.pk_where_sql))
}
}
pub trait TinyOrmDbModel {
const DB_META: TinyOrmDbMeta;
fn db_query(sql: &str) -> Query<TinyOrmDatabase, MySqlArguments> {
sqlx::query(sql)
}
}
#[async_trait]
pub trait TinyOrmDbQuery: TinyOrmDbModel {
async fn db_fetch_all<F>(
pool: &TinyOrmDbPool,
query: Query<'async_trait, TinyOrmDatabase, MySqlArguments>,
map: F,
) -> AnyhowResult<Vec<Self>>
where
Self: Sized + Send + Unpin,
F: FnMut(TinyOrmSqlRow) -> Self + Send,
{
query
.map(map)
.fetch_all(pool)
.await
.with_context(|| "查询数据库数据出错")
}
async fn db_fetch_all_row(
pool: &TinyOrmDbPool,
query: Query<'async_trait, TinyOrmDatabase, MySqlArguments>,
) -> AnyhowResult<Vec<TinyOrmSqlRow>>
where
Self: Sized + Send + Unpin,
{
query
.fetch_all(pool)
.await
.with_context(|| "查询数据库数据出错")
}
async fn db_fetch_one<F>(
pool: &TinyOrmDbPool,
query: Query<'async_trait, TinyOrmDatabase, MySqlArguments>,
map: F,
) -> AnyhowResult<Self>
where
Self: Sized + Send + Unpin,
F: FnMut(TinyOrmSqlRow) -> Self + Send,
{
query
.map(map)
.fetch_one(pool)
.await
.with_context(|| "查询数据库数据出错")
}
async fn db_fetch_one_row(
pool: &TinyOrmDbPool,
query: Query<'async_trait, TinyOrmDatabase, MySqlArguments>,
) -> AnyhowResult<TinyOrmSqlRow>
where
Self: Sized + Send + Unpin,
{
query
.fetch_one(pool)
.await
.with_context(|| "查询数据库数据出错")
}
async fn db_fetch_optional<F>(
pool: &TinyOrmDbPool,
query: Query<'async_trait, TinyOrmDatabase, MySqlArguments>,
map: F,
) -> AnyhowResult<Option<Self>>
where
Self: Sized + Send + Unpin,
F: FnMut(TinyOrmSqlRow) -> Self + Send,
{
query
.map(map)
.fetch_optional(pool)
.await
.with_context(|| "查询数据库数据出错")
}
async fn db_fetch_optional_row(
pool: &TinyOrmDbPool,
query: Query<'async_trait, TinyOrmDatabase, MySqlArguments>,
) -> AnyhowResult<Option<TinyOrmSqlRow>>
where
Self: Sized + Send + Unpin,
{
query
.fetch_optional(pool)
.await
.with_context(|| "查询数据库数据出错")
}
async fn db_get_all<F>(pool: &TinyOrmDbPool, map: F) -> AnyhowResult<Vec<Self>>
where
Self: Sized + Send + Unpin,
F: FnMut(TinyOrmSqlRow) -> Self + Send,
{
let sql = Self::DB_META.select_sql;
let query = Self::db_query(sql);
Self::db_fetch_all(pool, query, map).await
}
async fn db_execute(
pool: &TinyOrmDbPool,
query: Query<'async_trait, TinyOrmDatabase, MySqlArguments>,
) -> AnyhowResult<TinyOrmSqlResult>
where
Self: Sized + Send + Unpin,
{
query.execute(pool).await.with_context(|| "执行sql语句出错")
}
}
#[async_trait]
pub trait TinyOrmData: TinyOrmDbQuery {
fn orm_row_map(row: TinyOrmSqlRow) -> Self;
async fn orm_get_all(pool: &TinyOrmDbPool) -> AnyhowResult<Vec<Self>>
where
Self: Sized + Unpin,
{
Self::db_get_all(pool, Self::orm_row_map).await
}
async fn orm_get_with_sql<T>(
pool: &TinyOrmDbPool,
sql: &'async_trait str,
query_value: T,
) -> AnyhowResult<Self>
where
Self: Sized + Unpin,
T: 'async_trait
+ Send
+ sqlx::Type<TinyOrmDatabase>
+ sqlx::Encode<'async_trait, TinyOrmDatabase>
+ Sync,
{
let query = Self::db_query(sql).bind(query_value);
Self::db_fetch_one(pool, query, Self::orm_row_map).await
}
async fn orm_get_optional_with_sql<T>(
pool: &TinyOrmDbPool,
sql: &'async_trait str,
query_value: T,
) -> AnyhowResult<Option<Self>>
where
Self: Sized + Unpin,
T: 'async_trait
+ Send
+ sqlx::Type<TinyOrmDatabase>
+ sqlx::Encode<'async_trait, TinyOrmDatabase>
+ Sync,
{
let query = Self::db_query(sql).bind(query_value);
Self::db_fetch_optional(pool, query, Self::orm_row_map).await
}
async fn orm_filter_with_sql<T>(
pool: &TinyOrmDbPool,
sql: &'async_trait str,
query_value: T,
) -> AnyhowResult<Vec<Self>>
where
Self: Sized + Unpin,
T: 'async_trait
+ Send
+ sqlx::Type<TinyOrmDatabase>
+ sqlx::Encode<'async_trait, TinyOrmDatabase>
+ Sync,
{
let query = Self::db_query(sql).bind(query_value);
Self::db_fetch_all(pool, query, Self::orm_row_map).await
}
}
#[cfg(test)]
mod test;