1use crate::entity::{
9 EntityState, IEntitySnapshot, IEntityType, IFromRow, IGetKeyValues, INavigationSetter,
10};
11use crate::error::EFResult;
12use crate::provider::{DbValue, IDatabaseProvider};
13use crate::query::{BoolExpr, IQueryable, QueryBuilder};
14use std::collections::HashMap;
15use std::sync::Arc;
16
17pub trait IDbSet<T: IEntityType>: IQueryable<T> + Send + Sync {
26 fn add(&mut self, entity: T);
28
29 fn remove_all(&mut self);
31
32 fn remove_at(&mut self, index: usize) -> EFResult<()>;
34
35 fn attach(&mut self, entity: T);
37
38 fn added_entities(&self) -> Vec<&T>;
40
41 fn modified_entities(&self) -> Vec<&T>;
43
44 fn deleted_entities(&self) -> Vec<&T>;
46
47 fn entries_with_state(&self) -> Vec<(&T, EntityState)>;
49
50 fn clear_entries(&mut self);
52
53 fn len(&self) -> usize;
55
56 fn is_empty(&self) -> bool;
58}
59
60pub struct DbSet<T: IEntityType> {
65 pub(crate) entries: Vec<TrackedEntry<T>>,
66 table_name: String,
67 provider: Option<Arc<dyn IDatabaseProvider>>,
68 query_filter: Option<BoolExpr>,
69 filter_map: Option<Arc<HashMap<String, crate::query::CompiledFilter>>>,
70 lazy_loading_enabled: bool,
71}
72
73pub struct TrackedEntry<T: IEntityType> {
74 pub entity: T,
75 pub state: EntityState,
76 pub original: Option<HashMap<String, DbValue>>,
78 pub modified_properties: Vec<String>,
83 pub is_upsert: bool,
87}
88
89impl<T: IEntityType + IEntitySnapshot> DbSet<T> {
90 pub fn new(table_name: impl Into<String>) -> Self {
91 Self {
92 entries: Vec::new(),
93 table_name: table_name.into(),
94 provider: None,
95 query_filter: None,
96 filter_map: None,
97 lazy_loading_enabled: false,
98 }
99 }
100
101 pub fn with_provider(
102 table_name: impl Into<String>,
103 provider: Arc<dyn IDatabaseProvider>,
104 ) -> Self {
105 Self {
106 entries: Vec::new(),
107 table_name: table_name.into(),
108 provider: Some(provider),
109 query_filter: None,
110 filter_map: None,
111 lazy_loading_enabled: false,
112 }
113 }
114
115 pub fn set_query_filter(&mut self, filter: BoolExpr) {
116 self.query_filter = Some(filter);
117 }
118
119 pub fn set_filter_map(&mut self, map: Arc<HashMap<String, crate::query::CompiledFilter>>) {
122 self.filter_map = Some(map);
123 }
124
125 pub(crate) fn set_lazy_loading_enabled(&mut self, enabled: bool) {
127 self.lazy_loading_enabled = enabled;
128 }
129
130 pub(crate) fn query_filter(&self) -> Option<&BoolExpr> {
133 self.query_filter.as_ref()
134 }
135
136 pub fn set_provider(&mut self, provider: Arc<dyn IDatabaseProvider>) {
137 self.provider = Some(provider);
138 }
139
140 pub fn add(&mut self, entity: T) {
144 IDbSet::add(self, entity);
145 }
146
147 pub fn remove_all(&mut self) {
149 IDbSet::remove_all(self);
150 }
151
152 pub fn remove_at(&mut self, index: usize) -> EFResult<()> {
154 IDbSet::remove_at(self, index)
155 }
156
157 pub fn clear_entries(&mut self) {
159 IDbSet::clear_entries(self);
160 }
161
162 pub fn len(&self) -> usize {
164 IDbSet::len(self)
165 }
166
167 pub fn is_empty(&self) -> bool {
169 IDbSet::is_empty(self)
170 }
171
172 pub fn query(&self) -> QueryBuilder<T> {
174 IQueryable::query(self)
175 }
176
177 pub fn query_ignore_filters(&self) -> QueryBuilder<T> {
180 let qb = match &self.provider {
181 Some(p) => QueryBuilder::with_provider(&self.table_name, p.clone()),
182 None => QueryBuilder::new(&self.table_name),
183 };
184 qb.with_filter_map(self.filter_map.clone())
185 .with_lazy_loading(self.lazy_loading_enabled)
186 }
187
188 pub fn attach(&mut self, entity: T) {
189 IDbSet::attach(self, entity);
190 }
191
192 pub fn tracked_entries(&self) -> impl Iterator<Item = &T> {
193 self.entries.iter().map(|e| &e.entity)
194 }
195
196 pub fn tracked_entries_mut(&mut self) -> impl Iterator<Item = &mut T> {
197 self.entries.iter_mut().map(|e| &mut e.entity)
198 }
199
200 pub fn retain(&mut self, f: impl FnMut(&TrackedEntry<T>) -> bool) {
201 self.entries.retain(f);
202 }
203
204 pub fn remove_range(&mut self, entities: &[T])
206 where
207 T: PartialEq,
208 {
209 for entity in entities {
210 if let Some(entry) = self.entries.iter_mut().find(|e| e.entity == *entity) {
211 entry.state = EntityState::Deleted;
212 }
213 }
214 }
215
216 pub async fn load_all(&mut self) -> EFResult<()>
218 where
219 T: IFromRow
220 + INavigationSetter
221 + IGetKeyValues
222 + IEntitySnapshot
223 + crate::entity::ILazyInit,
224 {
225 let items = self.query().to_list().await?;
226 self.clear_entries();
227 for item in items {
228 self.attach(item);
229 }
230 Ok(())
231 }
232
233 pub fn update(&mut self, entity: T) {
235 self.entries.push(TrackedEntry {
236 entity,
237 state: EntityState::Modified,
238 original: None,
239 modified_properties: Vec::new(),
240 is_upsert: false,
241 });
242 }
243
244 pub fn upsert(&mut self, entity: T) {
251 self.entries.push(TrackedEntry {
252 entity,
253 state: EntityState::Added,
254 original: None,
255 modified_properties: Vec::new(),
256 is_upsert: true,
257 });
258 }
259
260 pub fn detect_changes(&mut self)
264 where
265 T: IEntitySnapshot,
266 {
267 for entry in &mut self.entries {
268 if entry.state != EntityState::Unchanged {
269 continue;
270 }
271 if let Some(ref original) = entry.original {
272 let current = entry.entity.snapshot();
273 if current == *original {
274 entry.modified_properties.clear();
275 continue;
276 }
277 let changed: Vec<String> = current
280 .iter()
281 .filter(|(k, v)| original.get(k.as_str()) != Some(v))
282 .map(|(k, _)| k.clone())
283 .collect();
284 if !changed.is_empty() {
285 entry.state = EntityState::Modified;
286 entry.modified_properties = changed;
287 }
288 }
289 }
290 }
291
292 #[allow(clippy::type_complexity)]
295 pub(crate) fn tracked_by_state(
296 &self,
297 state: EntityState,
298 ) -> Vec<(&T, Option<&HashMap<String, DbValue>>, &[String], bool)> {
299 self.entries
300 .iter()
301 .filter(|e| e.state == state)
302 .map(|e| {
303 (
304 &e.entity,
305 e.original.as_ref(),
306 e.modified_properties.as_slice(),
307 e.is_upsert,
308 )
309 })
310 .collect()
311 }
312
313 pub(crate) fn backfill_added_keys(&mut self, keys: &[i64])
320 where
321 T: IGetKeyValues,
322 {
323 let mut idx = 0usize;
324 for entry in &mut self.entries {
325 if entry.state != EntityState::Added || entry.is_upsert {
326 continue;
327 }
328 if let Some(&key) = keys.get(idx) {
329 if key != 0 {
330 entry.entity.set_auto_increment_key(key);
331 }
332 }
333 idx += 1;
334 }
335 }
336
337 pub(crate) fn accept_all_changes(&mut self)
346 where
347 T: IEntitySnapshot,
348 {
349 self.entries.retain(|e| e.state != EntityState::Deleted);
350 for entry in &mut self.entries {
351 if entry.state == EntityState::Added || entry.state == EntityState::Modified {
352 entry.state = EntityState::Unchanged;
353 entry.original = Some(entry.entity.snapshot());
354 entry.modified_properties.clear();
355 }
356 }
357 }
358 pub async fn exists_by_id(&self, key_values: HashMap<String, DbValue>) -> EFResult<bool>
359 where
360 T: IFromRow
361 + INavigationSetter
362 + IGetKeyValues
363 + IEntitySnapshot
364 + crate::entity::ILazyInit,
365 {
366 let pairs: Vec<(&str, DbValue)> = key_values
367 .iter()
368 .map(|(k, v)| (k.as_str(), v.clone()))
369 .collect();
370 Ok(self.query().find_by_key(&pairs).await?.is_some())
371 }
372
373 pub fn filter<F>(&self, apply: F) -> QueryBuilder<T>
375 where
376 F: FnOnce(QueryBuilder<T>) -> QueryBuilder<T>,
377 {
378 apply(self.query())
379 }
380}
381
382impl<T: IEntityType> IQueryable<T> for DbSet<T> {
387 fn query(&self) -> QueryBuilder<T> {
388 let mut qb = match &self.provider {
389 Some(p) => QueryBuilder::with_provider(&self.table_name, p.clone()),
390 None => QueryBuilder::new(&self.table_name),
391 };
392 if let Some(ref filter) = self.query_filter {
393 qb = qb.apply_query_filter(filter.clone());
394 }
395 qb.with_filter_map(self.filter_map.clone())
396 .with_lazy_loading(self.lazy_loading_enabled)
397 }
398}
399
400impl<T: IEntityType + IEntitySnapshot> IDbSet<T> for DbSet<T> {
405 fn add(&mut self, entity: T) {
406 self.entries.push(TrackedEntry {
407 entity,
408 state: EntityState::Added,
409 original: None,
410 modified_properties: Vec::new(),
411 is_upsert: false,
412 });
413 }
414
415 fn remove_all(&mut self) {
416 for entry in &mut self.entries {
417 entry.state = EntityState::Deleted;
418 }
419 }
420
421 fn remove_at(&mut self, index: usize) -> EFResult<()> {
422 if let Some(entry) = self.entries.get_mut(index) {
423 entry.state = EntityState::Deleted;
424 Ok(())
425 } else {
426 Err(crate::error::EFError::not_found(
427 "Entity not found at the given index".to_string(),
428 ))
429 }
430 }
431
432 fn attach(&mut self, entity: T) {
433 let original = entity.snapshot();
434 self.entries.push(TrackedEntry {
435 entity,
436 state: EntityState::Unchanged,
437 original: Some(original),
438 modified_properties: Vec::new(),
439 is_upsert: false,
440 });
441 }
442
443 fn added_entities(&self) -> Vec<&T> {
444 self.entries
445 .iter()
446 .filter(|e| e.state == EntityState::Added)
447 .map(|e| &e.entity)
448 .collect()
449 }
450
451 fn modified_entities(&self) -> Vec<&T> {
452 self.entries
453 .iter()
454 .filter(|e| e.state == EntityState::Modified)
455 .map(|e| &e.entity)
456 .collect()
457 }
458
459 fn deleted_entities(&self) -> Vec<&T> {
460 self.entries
461 .iter()
462 .filter(|e| e.state == EntityState::Deleted)
463 .map(|e| &e.entity)
464 .collect()
465 }
466
467 fn entries_with_state(&self) -> Vec<(&T, EntityState)> {
468 self.entries.iter().map(|e| (&e.entity, e.state)).collect()
469 }
470
471 fn clear_entries(&mut self) {
472 self.entries.clear();
473 }
474
475 fn len(&self) -> usize {
476 self.entries.len()
477 }
478
479 fn is_empty(&self) -> bool {
480 self.entries.is_empty()
481 }
482}