use burncloud_database_core::{
Repository, QueryContext, QueryOptions, QueryParam, QueryExecutor
};
use burncloud_database_core::error::DatabaseResult;
use async_trait::async_trait;
use serde::{Serialize, Deserialize};
use std::sync::Arc;
use std::marker::PhantomData;
pub struct BaseRepository<T>
where
T: Send + Sync + Serialize + for<'de> Deserialize<'de>,
{
pub query_executor: Arc<Box<dyn QueryExecutor>>,
table_name: String,
_phantom: PhantomData<T>,
}
impl<T> BaseRepository<T>
where
T: Send + Sync + Serialize + for<'de> Deserialize<'de>,
{
pub fn new(query_executor: Box<dyn QueryExecutor>, table_name: String) -> Self {
Self {
query_executor: Arc::new(query_executor),
table_name,
_phantom: PhantomData,
}
}
pub fn get_table_name(&self) -> &str {
&self.table_name
}
}
#[async_trait]
impl<T> Repository<T> for BaseRepository<T>
where
T: Send + Sync + Serialize + for<'de> Deserialize<'de>,
{
async fn find_by_id(&self, id: &str, context: &QueryContext) -> DatabaseResult<Option<T>> {
let query = format!("SELECT * FROM {} WHERE id = $1", self.get_table_name());
let id_param = burncloud_database_impl::StringParam(id.to_string());
let params: Vec<&dyn QueryParam> = vec![&id_param];
let result = self.query_executor.execute_query(&query, ¶ms, context).await?;
if result.rows.is_empty() {
Ok(None)
} else {
let row = &result.rows[0];
let entity: T = serde_json::from_value(serde_json::Value::Object(
row.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
)).map_err(|e| burncloud_database_core::DatabaseError::SerializationError(e.to_string()))?;
Ok(Some(entity))
}
}
async fn find_all(&self, options: &QueryOptions, context: &QueryContext) -> DatabaseResult<Vec<T>> {
let mut query = format!("SELECT * FROM {}", self.get_table_name());
if let Some(order_by) = &options.order_by {
query.push_str(&format!(" ORDER BY {}", order_by));
if let Some(direction) = &options.order_direction {
match direction {
burncloud_database_core::OrderDirection::Asc => query.push_str(" ASC"),
burncloud_database_core::OrderDirection::Desc => query.push_str(" DESC"),
}
}
}
if let Some(limit) = options.limit {
query.push_str(&format!(" LIMIT {}", limit));
}
if let Some(offset) = options.offset {
query.push_str(&format!(" OFFSET {}", offset));
}
let params: Vec<&dyn QueryParam> = vec![];
let result = self.query_executor.execute_query(&query, ¶ms, context).await?;
let mut entities = Vec::new();
for row in result.rows {
let entity: T = serde_json::from_value(serde_json::Value::Object(
row.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
)).map_err(|e| burncloud_database_core::DatabaseError::SerializationError(e.to_string()))?;
entities.push(entity);
}
Ok(entities)
}
async fn create(&self, entity: &T, context: &QueryContext) -> DatabaseResult<String> {
let entity_json = serde_json::to_value(entity)
.map_err(|e| burncloud_database_core::DatabaseError::SerializationError(e.to_string()))?;
if let serde_json::Value::Object(map) = entity_json {
let columns: Vec<String> = map.keys().cloned().collect();
let placeholders: Vec<String> = (1..=columns.len()).map(|i| format!("${}", i)).collect();
let query = format!(
"INSERT INTO {} ({}) VALUES ({}) RETURNING id",
self.get_table_name(),
columns.join(", "),
placeholders.join(", ")
);
let params: Vec<&dyn QueryParam> = vec![];
let result = self.query_executor.execute_query(&query, ¶ms, context).await?;
Ok(result.last_insert_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()))
} else {
Err(burncloud_database_core::DatabaseError::SerializationError(
"Entity must be a JSON object".to_string()
))
}
}
async fn update(&self, _id: &str, entity: &T, context: &QueryContext) -> DatabaseResult<()> {
let entity_json = serde_json::to_value(entity)
.map_err(|e| burncloud_database_core::DatabaseError::SerializationError(e.to_string()))?;
if let serde_json::Value::Object(map) = entity_json {
let set_clauses: Vec<String> = map.keys().enumerate()
.map(|(i, key)| format!("{} = ${}", key, i + 1))
.collect();
let query = format!(
"UPDATE {} SET {} WHERE id = ${}",
self.get_table_name(),
set_clauses.join(", "),
map.len() + 1
);
let params: Vec<&dyn QueryParam> = vec![];
self.query_executor.execute_query(&query, ¶ms, context).await?;
Ok(())
} else {
Err(burncloud_database_core::DatabaseError::SerializationError(
"Entity must be a JSON object".to_string()
))
}
}
async fn delete(&self, id: &str, context: &QueryContext) -> DatabaseResult<()> {
let query = format!("DELETE FROM {} WHERE id = $1", self.get_table_name());
let id_param = burncloud_database_impl::StringParam(id.to_string());
let params: Vec<&dyn QueryParam> = vec![&id_param];
self.query_executor.execute_query(&query, ¶ms, context).await?;
Ok(())
}
async fn exists(&self, id: &str, context: &QueryContext) -> DatabaseResult<bool> {
let query = format!("SELECT 1 FROM {} WHERE id = $1 LIMIT 1", self.get_table_name());
let id_param = burncloud_database_impl::StringParam(id.to_string());
let params: Vec<&dyn QueryParam> = vec![&id_param];
let result = self.query_executor.execute_query(&query, ¶ms, context).await?;
Ok(!result.rows.is_empty())
}
}