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}
79
80impl<T: IEntityType + IEntitySnapshot> DbSet<T> {
81 pub fn new(table_name: impl Into<String>) -> Self {
82 Self {
83 entries: Vec::new(),
84 table_name: table_name.into(),
85 provider: None,
86 query_filter: None,
87 filter_map: None,
88 lazy_loading_enabled: false,
89 }
90 }
91
92 pub fn with_provider(
93 table_name: impl Into<String>,
94 provider: Arc<dyn IDatabaseProvider>,
95 ) -> Self {
96 Self {
97 entries: Vec::new(),
98 table_name: table_name.into(),
99 provider: Some(provider),
100 query_filter: None,
101 filter_map: None,
102 lazy_loading_enabled: false,
103 }
104 }
105
106 pub fn set_query_filter(&mut self, filter: BoolExpr) {
107 self.query_filter = Some(filter);
108 }
109
110 pub fn set_filter_map(&mut self, map: Arc<HashMap<String, crate::query::CompiledFilter>>) {
113 self.filter_map = Some(map);
114 }
115
116 pub(crate) fn set_lazy_loading_enabled(&mut self, enabled: bool) {
118 self.lazy_loading_enabled = enabled;
119 }
120
121 pub(crate) fn query_filter(&self) -> Option<&BoolExpr> {
124 self.query_filter.as_ref()
125 }
126
127 pub fn set_provider(&mut self, provider: Arc<dyn IDatabaseProvider>) {
128 self.provider = Some(provider);
129 }
130
131 pub fn add(&mut self, entity: T) {
135 IDbSet::add(self, entity);
136 }
137
138 pub fn remove_all(&mut self) {
140 IDbSet::remove_all(self);
141 }
142
143 pub fn remove_at(&mut self, index: usize) -> EFResult<()> {
145 IDbSet::remove_at(self, index)
146 }
147
148 pub fn clear_entries(&mut self) {
150 IDbSet::clear_entries(self);
151 }
152
153 pub fn len(&self) -> usize {
155 IDbSet::len(self)
156 }
157
158 pub fn is_empty(&self) -> bool {
160 IDbSet::is_empty(self)
161 }
162
163 pub fn query(&self) -> QueryBuilder<T> {
165 IQueryable::query(self)
166 }
167
168 pub fn query_ignore_filters(&self) -> QueryBuilder<T> {
171 let qb = match &self.provider {
172 Some(p) => QueryBuilder::with_provider(&self.table_name, p.clone()),
173 None => QueryBuilder::new(&self.table_name),
174 };
175 qb.with_filter_map(self.filter_map.clone())
176 .with_lazy_loading(self.lazy_loading_enabled)
177 }
178
179 pub fn attach(&mut self, entity: T) {
180 IDbSet::attach(self, entity);
181 }
182
183 pub fn tracked_entries(&self) -> impl Iterator<Item = &T> {
184 self.entries.iter().map(|e| &e.entity)
185 }
186
187 pub fn tracked_entries_mut(&mut self) -> impl Iterator<Item = &mut T> {
188 self.entries.iter_mut().map(|e| &mut e.entity)
189 }
190
191 pub fn retain(&mut self, f: impl FnMut(&TrackedEntry<T>) -> bool) {
192 self.entries.retain(f);
193 }
194
195 pub fn remove_range(&mut self, entities: &[T])
197 where
198 T: PartialEq,
199 {
200 for entity in entities {
201 if let Some(entry) = self.entries.iter_mut().find(|e| e.entity == *entity) {
202 entry.state = EntityState::Deleted;
203 }
204 }
205 }
206
207 pub async fn load_all(&mut self) -> EFResult<()>
209 where
210 T: IFromRow
211 + INavigationSetter
212 + IGetKeyValues
213 + IEntitySnapshot
214 + crate::entity::ILazyInit,
215 {
216 let items = self.query().to_list().await?;
217 self.clear_entries();
218 for item in items {
219 self.attach(item);
220 }
221 Ok(())
222 }
223
224 pub fn update(&mut self, entity: T) {
226 self.entries.push(TrackedEntry {
227 entity,
228 state: EntityState::Modified,
229 original: None,
230 });
231 }
232
233 pub fn detect_changes(&mut self)
235 where
236 T: IEntitySnapshot,
237 {
238 for entry in &mut self.entries {
239 if entry.state != EntityState::Unchanged {
240 continue;
241 }
242 if let Some(ref original) = entry.original {
243 if entry.entity.snapshot() != *original {
244 entry.state = EntityState::Modified;
245 }
246 }
247 }
248 }
249
250 pub(crate) fn tracked_by_state(
252 &self,
253 state: EntityState,
254 ) -> Vec<(&T, Option<&HashMap<String, DbValue>>)> {
255 self.entries
256 .iter()
257 .filter(|e| e.state == state)
258 .map(|e| (&e.entity, e.original.as_ref()))
259 .collect()
260 }
261 pub async fn exists_by_id(&self, key_values: HashMap<String, DbValue>) -> EFResult<bool>
262 where
263 T: IFromRow
264 + INavigationSetter
265 + IGetKeyValues
266 + IEntitySnapshot
267 + crate::entity::ILazyInit,
268 {
269 let pairs: Vec<(&str, DbValue)> = key_values
270 .iter()
271 .map(|(k, v)| (k.as_str(), v.clone()))
272 .collect();
273 Ok(self.query().find_by_key(&pairs).await?.is_some())
274 }
275
276 pub fn filter<F>(&self, apply: F) -> QueryBuilder<T>
278 where
279 F: FnOnce(QueryBuilder<T>) -> QueryBuilder<T>,
280 {
281 apply(self.query())
282 }
283}
284
285impl<T: IEntityType> IQueryable<T> for DbSet<T> {
290 fn query(&self) -> QueryBuilder<T> {
291 let mut qb = match &self.provider {
292 Some(p) => QueryBuilder::with_provider(&self.table_name, p.clone()),
293 None => QueryBuilder::new(&self.table_name),
294 };
295 if let Some(ref filter) = self.query_filter {
296 qb = qb.apply_query_filter(filter.clone());
297 }
298 qb.with_filter_map(self.filter_map.clone())
299 .with_lazy_loading(self.lazy_loading_enabled)
300 }
301}
302
303impl<T: IEntityType + IEntitySnapshot> IDbSet<T> for DbSet<T> {
308 fn add(&mut self, entity: T) {
309 self.entries.push(TrackedEntry {
310 entity,
311 state: EntityState::Added,
312 original: None,
313 });
314 }
315
316 fn remove_all(&mut self) {
317 for entry in &mut self.entries {
318 entry.state = EntityState::Deleted;
319 }
320 }
321
322 fn remove_at(&mut self, index: usize) -> EFResult<()> {
323 if let Some(entry) = self.entries.get_mut(index) {
324 entry.state = EntityState::Deleted;
325 Ok(())
326 } else {
327 Err(crate::error::EFError::not_found(
328 "Entity not found at the given index".to_string(),
329 ))
330 }
331 }
332
333 fn attach(&mut self, entity: T) {
334 let original = entity.snapshot();
335 self.entries.push(TrackedEntry {
336 entity,
337 state: EntityState::Unchanged,
338 original: Some(original),
339 });
340 }
341
342 fn added_entities(&self) -> Vec<&T> {
343 self.entries
344 .iter()
345 .filter(|e| e.state == EntityState::Added)
346 .map(|e| &e.entity)
347 .collect()
348 }
349
350 fn modified_entities(&self) -> Vec<&T> {
351 self.entries
352 .iter()
353 .filter(|e| e.state == EntityState::Modified)
354 .map(|e| &e.entity)
355 .collect()
356 }
357
358 fn deleted_entities(&self) -> Vec<&T> {
359 self.entries
360 .iter()
361 .filter(|e| e.state == EntityState::Deleted)
362 .map(|e| &e.entity)
363 .collect()
364 }
365
366 fn entries_with_state(&self) -> Vec<(&T, EntityState)> {
367 self.entries.iter().map(|e| (&e.entity, e.state)).collect()
368 }
369
370 fn clear_entries(&mut self) {
371 self.entries.clear();
372 }
373
374 fn len(&self) -> usize {
375 self.entries.len()
376 }
377
378 fn is_empty(&self) -> bool {
379 self.entries.is_empty()
380 }
381}