use crate::merge_any_arguments;
use crate::{Entity, Location, Mutation, PageInfo, PagedList, Primary, SelectedEntity, Selection};
use async_trait::async_trait;
use sqlx::Any;
use sqlx::AnyExecutor;
use sqlx::Database;
use sqlx::Error;
use sqlx::Executor;
use sqlx::{any::AnyRow, Row};
#[async_trait]
pub trait GenericDaoMapper {
async fn select<'e, EX, P, S, SE>(
executor: EX,
primary: P,
selection: S,
) -> Result<Option<SE>, Error>
where
EX: 'e + Executor<'e, Database = Any>,
P: Primary + Send,
S: Selection + Send,
SE: SelectedEntity + Send + Unpin,
{
let select_clause = selection.get_sql_selection('`');
let where_clause = primary.get_where_clause('`', "?");
let table_name = primary.table_name('`');
let select_stmt = &format!(
"SELECT {} FROM {} WHERE {}",
select_clause, table_name, where_clause
);
let args = primary.into_any_arguments();
let sqlx_query =
sqlx::query_with(select_stmt, args).try_map(|row: AnyRow| SE::from_any_row(row));
let entity_opt: Option<SE> = sqlx_query.fetch_optional(executor).await?;
return Ok(entity_opt);
}
async fn create<'e, EX, E>(executor: EX, entity: E) -> Result<E, Error>
where
EX: 'e + Executor<'e, Database = Any>,
E: Entity + Send + Clone,
{
let table_name = entity.table_name('`');
let field_names = entity.get_fields_string('`');
let question_marks = entity
.get_fields_name()
.iter()
.map(|_| "?".to_string())
.collect::<Vec<String>>()
.join(", ");
let insert_stmt = &format!(
"INSERT INTO {} ({}) VALUES({})",
table_name, field_names, question_marks
);
let args = entity.clone().into_insert_any_arguments();
let _ = sqlx::query_with(insert_stmt, args)
.execute(executor)
.await?;
return Ok(entity);
}
async fn insert<'e, EX, E>(executor: EX, entity: E) -> Result<bool, Error>
where
EX: 'e + Executor<'e, Database = Any>,
E: Entity + Send + Clone,
{
let table_name = entity.table_name('`');
let field_names = entity.get_fields_string('`');
let question_marks = entity
.get_fields_name()
.iter()
.map(|_| "?".to_string())
.collect::<Vec<String>>()
.join(", ");
let insert_stmt = &format!(
"INSERT INTO {} ({}) VALUES({})",
table_name, field_names, question_marks
);
let args = entity.clone().into_insert_any_arguments();
let result = sqlx::query_with(insert_stmt, args)
.execute(executor)
.await?;
return Ok(result.rows_affected() > 0);
}
async fn upsert<'e, EX, E>(executor: EX, entity: E) -> Result<bool, Error>
where
EX: 'e + Executor<'e, Database = Any>,
E: Entity + Send + Clone,
{
let table_name = entity.table_name('`');
let field_names = entity.get_fields_string('`');
let question_marks = entity
.get_fields_name()
.iter()
.map(|_| "?".to_string())
.collect::<Vec<String>>()
.join(", ");
let primary_fields_string = entity.get_primary_fields_string('`');
let upsert_assign_clause = entity.get_body_assignment_clause('`', "?");
let upsert_stmt = &format!(
"INSERT INTO {}({}) VALUES({})
ON CONFLICT({}) DO UPDATE SET {}",
table_name, field_names, question_marks, primary_fields_string, upsert_assign_clause
);
let args = entity.clone().into_upsert_any_arguments();
let result = sqlx::query_with(upsert_stmt, args)
.execute(executor)
.await?;
return Ok(result.rows_affected() > 0);
}
async fn update<'e, EX, E>(executor: EX, entity: E) -> Result<bool, Error>
where
EX: 'e + Executor<'e, Database = Any>,
E: Entity + Send + Clone,
{
let table_name = entity.table_name('`');
let body_assign_clause = entity.get_body_assignment_clause('`', "?");
let primary_assign_clause = entity.get_primary_assignment_clause('`', "?");
let update_stmt = &format!(
"UPDATE {} SET {} WHERE {}",
table_name, body_assign_clause, primary_assign_clause
);
let args = entity.into_update_any_arguments();
let result = sqlx::query_with(update_stmt, args)
.execute(executor)
.await?;
return Ok(result.rows_affected() > 0);
}
async fn remove<'e, EX, P, E>(executor: EX, primary: P) -> Result<E, Error>
where
EX: 'e + Executor<'e, Database = Any>,
P: Primary + Send,
E: Entity + Send + Clone,
{
todo!()
}
async fn delete<'e, EX, P>(&self, executor: EX, primary: P) -> Result<bool, Error>
where
EX: 'e + Executor<'e, Database = Any>,
P: Primary + Send,
{
let table_name = primary.table_name('`');
let where_clause = primary.get_where_clause('`', "?");
let delete_stmt = &format!("DELETE FROM {} WHERE {}", table_name, where_clause);
let args = primary.into_any_arguments();
let result = sqlx::query_with(delete_stmt, args)
.execute(executor)
.await?;
return Ok(result.rows_affected() > 0);
}
async fn search<'e, EX, L, S, SE>(
executor: EX,
location: L,
selection: S,
) -> Result<Vec<SE>, Error>
where
EX: 'e + Executor<'e, Database = Any>,
L: Location + Send,
S: Selection + Send,
SE: SelectedEntity + Send + Unpin,
{
let table_name = location.table_name('`');
let selected_fields = selection.get_sql_selection('`');
let where_clause = location.get_where_clause('`', "?");
let search_stmt = &format!(
"SELECT {} FROM {} WHERE {}",
selected_fields, table_name, where_clause
);
let args = location.into_any_arguments();
let sqlx_query =
sqlx::query_with(search_stmt, args).try_map(|row: AnyRow| SE::from_any_row(row));
let entity_list = sqlx_query.fetch_all(executor).await?;
return Ok(entity_list);
}
async fn search_paged<'e, EX, L, S, SE>(
executor: EX,
location: L,
selection: S,
) -> Result<PagedList<SE>, Error>
where
EX: 'e + Executor<'e, Database = Any>,
L: Location + Send,
S: Selection + Send,
SE: SelectedEntity + Send + Unpin,
{
let table_name = location.table_name('`');
let selected_fields = selection.get_sql_selection('`');
let where_clause = location.get_where_clause('`', "?");
let search_stmt = &format!(
"SELECT {} FROM {} WHERE {}",
selected_fields, table_name, where_clause
);
let args = location.into_any_arguments();
let sqlx_query =
sqlx::query_with(search_stmt, args).try_map(|row: AnyRow| SE::from_any_row(row));
let entity_list = sqlx_query.fetch_all(executor).await?;
let page_info = PageInfo {
page_size: 10,
page_num: 10,
page_total: 10,
total: 100,
};
return Ok(PagedList {
data: entity_list,
page: page_info,
});
}
async fn purify<'e, EX, L>(executor: EX, location: L) -> Result<usize, Error>
where
EX: 'e + Executor<'e, Database = Any>,
L: Location + Send,
{
let table_name = location.table_name('`');
let where_clause = location.get_where_clause('`', "?");
let delete_stmt = &format!("DELETE FROM {} WHERE {}", table_name, where_clause);
let args = location.into_any_arguments();
let sqlx_query = sqlx::query_with(delete_stmt, args);
let result = sqlx_query.execute(executor).await?;
return Ok(result.rows_affected() as usize);
}
async fn change<'e, EX, L, M>(executor: EX, location: L, mutation: M) -> Result<usize, Error>
where
EX: 'e + Executor<'e, Database = Any>,
L: Location + Send,
M: Mutation + Send,
{
let table_name = location.table_name('`');
let update_clause = mutation.get_update_clause('`', "?");
let where_clause = location.get_where_clause('`', "?");
let change_stmt = &format!(
"UPDATE {} SET {} WHERE {}",
table_name, update_clause, where_clause
);
let mutation_args = mutation.into_any_arguments();
let location_args = location.into_any_arguments();
let args = merge_any_arguments(mutation_args, location_args);
let sqlx_query = sqlx::query_with(change_stmt, args);
let result = sqlx_query.execute(executor).await?;
return Ok(result.rows_affected() as usize);
}
}