use crate::{
adapter::DatabaseAdapter,
error::Result,
model::{Model, TableSchema},
query::{CondOp, QueryBuilder},
value::Value,
};
use std::sync::Arc;
pub struct Repository<M: Model, A: DatabaseAdapter> {
adapter: Arc<A>,
_phantom: std::marker::PhantomData<M>,
}
impl<M: Model, A: DatabaseAdapter> Repository<M, A> {
pub fn new(adapter: Arc<A>) -> Self {
Self { adapter, _phantom: std::marker::PhantomData }
}
pub fn adapter(&self) -> &A {
&self.adapter
}
pub async fn migrate(&self) -> Result<()> {
let schema = TableSchema::from_model::<M>();
self.adapter.create_table(&schema).await
}
pub async fn insert(&self, model: &M) -> Result<M> {
let row = model.to_row()?;
let inserted = self.adapter.insert(M::table_name(), row).await?;
M::from_row(inserted)
}
pub async fn insert_many(&self, models: &[M]) -> Result<u64> {
let rows: Result<Vec<_>> = models.iter().map(|m| m.to_row()).collect();
self.adapter.insert_many(M::table_name(), rows?).await
}
pub async fn find_all(&self) -> Result<Vec<M>> {
let qb = QueryBuilder::table(M::table_name());
let rows = self.adapter.find(&qb).await?;
rows.into_iter().map(M::from_row).collect()
}
pub async fn find_by_id(&self, id: impl Into<Value>) -> Result<Option<M>> {
let qb = QueryBuilder::table(M::table_name())
.r#where(M::primary_key(), CondOp::Eq, id.into());
let row = self.adapter.find_one(&qb).await?;
row.map(M::from_row).transpose()
}
pub async fn find_one(&self, qb: QueryBuilder) -> Result<Option<M>> {
let row = self.adapter.find_one(&qb).await?;
row.map(M::from_row).transpose()
}
pub async fn find(&self, qb: QueryBuilder) -> Result<Vec<M>> {
let rows = self.adapter.find(&qb).await?;
rows.into_iter().map(M::from_row).collect()
}
pub async fn paginate(&self, page: u64, per_page: u64) -> Result<Vec<M>> {
let qb = QueryBuilder::table(M::table_name())
.limit(per_page)
.offset((page.saturating_sub(1)) * per_page);
let rows = self.adapter.find(&qb).await?;
rows.into_iter().map(M::from_row).collect()
}
pub async fn count(&self, qb: Option<QueryBuilder>) -> Result<u64> {
let qb = qb.unwrap_or_else(|| QueryBuilder::table(M::table_name()));
self.adapter.count(&qb).await
}
pub async fn update(&self, qb: QueryBuilder) -> Result<u64> {
self.adapter.update(&qb).await
}
pub async fn save(&self, model: &M) -> Result<M> {
let pk = model.pk_value();
match pk {
Ok(Value::Null) | Err(_) => self.insert(model).await,
Ok(pk_val) => {
let row = model.to_row()?;
let mut qb = QueryBuilder::table(M::table_name())
.r#where(M::primary_key(), CondOp::Eq, pk_val)
.operation(crate::query::Operation::Update);
for (col, val) in row {
if col != M::primary_key() {
qb = qb.set(col, val);
}
}
self.adapter.update(&qb).await?;
self.find_by_id(model.pk_value()?).await?.ok_or_else(|| {
crate::error::RusticxError::NotFound("record after save".to_owned())
})
}
}
}
pub async fn delete_by_id(&self, id: impl Into<Value>) -> Result<u64> {
let qb = QueryBuilder::table(M::table_name())
.r#where(M::primary_key(), CondOp::Eq, id.into())
.operation(crate::query::Operation::Delete);
self.adapter.delete(&qb).await
}
pub async fn delete(&self, qb: QueryBuilder) -> Result<u64> {
self.adapter.delete(&qb).await
}
pub fn query(&self) -> QueryBuilder {
QueryBuilder::table(M::table_name())
}
}