use crate::entity::{IEntitySnapshot, IEntityType, IFromRow, IGetKeyValues, INavigationSetter};
use crate::error::EFResult;
use crate::provider::{DbValue, IDatabaseProvider};
use crate::query::{BoolExpr, IQueryable, QueryBuilder};
use std::collections::HashMap;
use std::sync::Arc;
pub struct DbSetEntry<T: IEntityType> {
pub entity: T,
pub entry_id: u64,
}
pub trait IDbSet<T: IEntityType>: IQueryable<T> + Send + Sync {
fn clear_entries(&mut self);
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
}
pub struct DbSet<T: IEntityType> {
pub(crate) entries: Vec<DbSetEntry<T>>,
table_name: String,
provider: Option<Arc<dyn IDatabaseProvider>>,
query_filter: Option<BoolExpr>,
filter_map: Option<Arc<HashMap<String, crate::query::CompiledFilter>>>,
lazy_loading_enabled: bool,
}
impl<T: IEntityType + IEntitySnapshot> DbSet<T> {
pub fn new(table_name: impl Into<String>) -> Self {
Self {
entries: Vec::new(),
table_name: table_name.into(),
provider: None,
query_filter: None,
filter_map: None,
lazy_loading_enabled: false,
}
}
pub fn with_provider(
table_name: impl Into<String>,
provider: Arc<dyn IDatabaseProvider>,
) -> Self {
Self {
entries: Vec::new(),
table_name: table_name.into(),
provider: Some(provider),
query_filter: None,
filter_map: None,
lazy_loading_enabled: false,
}
}
pub fn set_query_filter(&mut self, filter: BoolExpr) {
self.query_filter = Some(filter);
}
pub fn set_filter_map(&mut self, map: Arc<HashMap<String, crate::query::CompiledFilter>>) {
self.filter_map = Some(map);
}
pub(crate) fn set_lazy_loading_enabled(&mut self, enabled: bool) {
self.lazy_loading_enabled = enabled;
}
pub(crate) fn query_filter(&self) -> Option<&BoolExpr> {
self.query_filter.as_ref()
}
pub fn set_provider(&mut self, provider: Arc<dyn IDatabaseProvider>) {
self.provider = Some(provider);
}
pub(crate) fn push_entry(&mut self, entity: T, entry_id: u64) {
self.entries.push(DbSetEntry { entity, entry_id });
}
pub(crate) fn entry_id_at(&self, idx: usize) -> Option<u64> {
self.entries.get(idx).map(|e| e.entry_id)
}
pub fn tracked_entries(&self) -> impl Iterator<Item = &T> {
self.entries.iter().map(|e| &e.entity)
}
pub fn tracked_entries_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.entries.iter_mut().map(|e| &mut e.entity)
}
pub fn retain(&mut self, f: impl FnMut(&DbSetEntry<T>) -> bool) {
self.entries.retain(f);
}
pub fn clear_entries(&mut self) {
IDbSet::clear_entries(self);
}
pub fn len(&self) -> usize {
IDbSet::len(self)
}
pub fn is_empty(&self) -> bool {
IDbSet::is_empty(self)
}
pub fn query(&self) -> QueryBuilder<T> {
IQueryable::query(self)
}
pub fn query_ignore_filters(&self) -> QueryBuilder<T> {
let qb = match &self.provider {
Some(p) => QueryBuilder::with_provider(&self.table_name, p.clone()),
None => QueryBuilder::new(&self.table_name),
};
qb.with_filter_map(self.filter_map.clone())
.with_lazy_loading(self.lazy_loading_enabled)
}
pub fn filter<F>(&self, apply: F) -> QueryBuilder<T>
where
F: FnOnce(QueryBuilder<T>) -> QueryBuilder<T>,
{
apply(self.query())
}
pub async fn exists_by_id(&self, key_values: HashMap<String, DbValue>) -> EFResult<bool>
where
T: IFromRow
+ INavigationSetter
+ IGetKeyValues
+ IEntitySnapshot
+ crate::entity::ILazyInit,
{
let pairs: Vec<(&str, DbValue)> = key_values
.iter()
.map(|(k, v)| (k.as_str(), v.clone()))
.collect();
Ok(self.query().find_by_key(&pairs).await?.is_some())
}
}
impl<T: IEntityType> IQueryable<T> for DbSet<T> {
fn query(&self) -> QueryBuilder<T> {
let mut qb = match &self.provider {
Some(p) => QueryBuilder::with_provider(&self.table_name, p.clone()),
None => QueryBuilder::new(&self.table_name),
};
if let Some(ref filter) = self.query_filter {
qb = qb.apply_query_filter(filter.clone());
}
qb.with_filter_map(self.filter_map.clone())
.with_lazy_loading(self.lazy_loading_enabled)
}
}
impl<T: IEntityType + IEntitySnapshot> IDbSet<T> for DbSet<T> {
fn clear_entries(&mut self) {
self.entries.clear();
}
fn len(&self) -> usize {
self.entries.len()
}
fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}