1use std::collections::BTreeMap;
2use std::hash::Hash;
3use std::ops::{Index, IndexMut};
4
5use crate::{Entity, IdentifiableEntity, Record, Value, VersionedEntity};
6
7#[derive(Debug, Clone, PartialEq)]
8pub struct SmartList<T> {
9 pub data: Vec<T>,
10 pub total_count: Option<u64>,
11 pub aggregations: Record,
12 pub summary: Record,
13 pub facets: BTreeMap<String, SmartList<Record>>,
14 pub is_loaded: bool,
15}
16
17impl<T> SmartList<T> {
18 pub fn empty() -> Self {
19 let mut list = Self::new(Vec::new());
20 list.is_loaded = false;
21 list
22 }
23
24 pub fn into_value(self) -> Option<Value>
25 where
26 T: Entity,
27 {
28 let mut has_changes = false;
29 let mut items = Vec::new();
30 for entity in self.data.into_iter() {
31 if Entity::is_new(&entity)
32 || Entity::dirty_fields(&entity).is_some()
33 || Entity::is_marked_as_delete(&entity)
34 {
35 has_changes = true;
36 }
37 items.push(Value::object(Entity::into_record(entity)));
38 }
39
40 (self.is_loaded || has_changes).then_some(Value::List(items))
41 }
42
43 pub fn new(data: Vec<T>) -> Self {
44 Self {
45 data,
46 total_count: None,
47 aggregations: Record::new(),
48 summary: Record::new(),
49 facets: BTreeMap::new(),
50 is_loaded: true,
51 }
52 }
53
54 pub fn with_total_count(mut self, total_count: u64) -> Self {
55 self.total_count = Some(total_count);
56 self
57 }
58
59 pub fn with_aggregation(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
60 self.aggregations.insert(key.into(), value.into());
61 self
62 }
63
64 pub fn with_summary(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
65 self.summary.insert(key.into(), value.into());
66 self
67 }
68
69 pub fn with_facet(mut self, key: impl Into<String>, facet: SmartList<Record>) -> Self {
70 self.facets.insert(key.into(), facet);
71 self
72 }
73
74 pub fn add_facet(&mut self, key: impl Into<String>, facet: SmartList<Record>) {
75 self.facets.insert(key.into(), facet);
76 }
77
78 pub fn facets(&self) -> &BTreeMap<String, SmartList<Record>> {
79 &self.facets
80 }
81
82 pub fn facets_mut(&mut self) -> &mut BTreeMap<String, SmartList<Record>> {
83 &mut self.facets
84 }
85
86 pub fn facet(&self, key: &str) -> Option<&SmartList<Record>> {
87 self.facets.get(key)
88 }
89
90 pub fn facet_mut(&mut self, key: &str) -> Option<&mut SmartList<Record>> {
91 self.facets.get_mut(key)
92 }
93
94 pub fn remove_facet(&mut self, key: &str) -> Option<SmartList<Record>> {
95 self.facets.remove(key)
96 }
97
98 pub fn take_facets(&mut self) -> BTreeMap<String, SmartList<Record>> {
99 std::mem::take(&mut self.facets)
100 }
101
102 pub fn push(&mut self, value: T) {
103 self.data.push(value);
104 }
105
106 pub fn extend(&mut self, values: impl IntoIterator<Item = T>) {
107 self.data.extend(values);
108 }
109
110 pub fn set(&mut self, index: usize, value: T) -> Option<T> {
111 if index >= self.data.len() {
112 return None;
113 }
114 Some(std::mem::replace(&mut self.data[index], value))
115 }
116
117 pub fn get(&self, index: usize) -> Option<&T> {
118 self.data.get(index)
119 }
120
121 pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
122 self.data.get_mut(index)
123 }
124
125 pub fn last(&self) -> Option<&T> {
126 self.data.last()
127 }
128
129 pub fn len(&self) -> usize {
130 self.data.len()
131 }
132
133 pub fn is_empty(&self) -> bool {
134 self.data.is_empty()
135 }
136
137 pub fn first(&self) -> Option<&T> {
138 self.data.first()
139 }
140
141 pub fn iter(&self) -> std::slice::Iter<'_, T> {
142 self.data.iter()
143 }
144
145 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
146 self.data.iter_mut()
147 }
148
149 pub fn as_slice(&self) -> &[T] {
150 &self.data
151 }
152
153 pub fn as_mut_slice(&mut self) -> &mut [T] {
154 &mut self.data
155 }
156
157 pub fn retain(&mut self, filter: impl FnMut(&T) -> bool) {
158 self.data.retain(filter);
159 }
160
161 pub fn total_count_or_len(&self) -> u64 {
162 self.total_count.unwrap_or(self.data.len() as u64)
163 }
164
165 pub fn aggregation(&self, key: &str) -> Option<&Value> {
166 self.aggregations.get(key)
167 }
168
169 pub fn summary(&self, key: &str) -> Option<&Value> {
170 self.summary.get(key)
171 }
172
173 pub fn aggregation_json(&self) -> serde_json::Value {
174 crate::record_to_json_value(&self.aggregations)
175 }
176
177 pub fn summary_json(&self) -> serde_json::Value {
178 crate::record_to_json_value(&self.summary)
179 }
180
181 pub fn into_vec(self) -> Vec<T> {
182 self.data
183 }
184
185 pub fn map<U>(self, mapper: impl FnMut(T) -> U) -> SmartList<U> {
186 SmartList {
187 data: self.data.into_iter().map(mapper).collect(),
188 total_count: self.total_count,
189 aggregations: self.aggregations,
190 summary: self.summary,
191 facets: self.facets,
192 is_loaded: self.is_loaded,
193 }
194 }
195
196 pub fn to_list<U>(&self, mapper: impl FnMut(&T) -> U) -> Vec<U> {
197 self.data.iter().map(mapper).collect()
198 }
199
200 pub fn to_set<U>(&self, mapper: impl FnMut(&T) -> U) -> std::collections::BTreeSet<U>
201 where
202 U: Ord,
203 {
204 self.data.iter().map(mapper).collect()
205 }
206
207 pub fn identity_map<K>(&self, mut key: impl FnMut(&T) -> K) -> BTreeMap<K, T>
208 where
209 K: Ord,
210 T: Clone,
211 {
212 self.data
213 .iter()
214 .map(|item| (key(item), item.clone()))
215 .collect()
216 }
217
218 pub fn group_by<K>(&self, mut key: impl FnMut(&T) -> K) -> BTreeMap<K, Vec<T>>
219 where
220 K: Ord,
221 T: Clone,
222 {
223 let mut groups = BTreeMap::new();
224 for item in &self.data {
225 groups
226 .entry(key(item))
227 .or_insert_with(Vec::new)
228 .push(item.clone());
229 }
230 groups
231 }
232
233 pub fn merge_by<K>(
234 &mut self,
235 incoming: impl IntoIterator<Item = T>,
236 mut key: impl FnMut(&T) -> K,
237 ) where
238 K: Eq + Hash,
239 {
240 let mut positions = self
241 .data
242 .iter()
243 .enumerate()
244 .map(|(index, item)| (key(item), index))
245 .collect::<std::collections::HashMap<_, _>>();
246 for item in incoming {
247 let item_key = key(&item);
248 match positions.get(&item_key).copied() {
249 Some(index) => self.data[index] = item,
250 None => {
251 positions.insert(item_key, self.data.len());
252 self.data.push(item);
253 }
254 }
255 }
256 }
257
258 pub fn into_records(self) -> SmartList<Record>
259 where
260 T: Entity,
261 {
262 SmartList {
263 data: self.data.into_iter().map(Entity::into_record).collect(),
264 total_count: self.total_count,
265 aggregations: self.aggregations,
266 summary: self.summary,
267 facets: self.facets,
268 is_loaded: self.is_loaded,
269 }
270 }
271}
272
273impl<T> SmartList<T>
274where
275 T: IdentifiableEntity,
276{
277 pub fn ids(&self) -> Vec<Value> {
278 self.data.iter().map(IdentifiableEntity::id_value).collect()
279 }
280
281 pub fn map_by_id(&self) -> BTreeMap<String, T>
282 where
283 T: Clone,
284 {
285 self.data
286 .iter()
287 .map(|item| (id_key(&item.id_value()), item.clone()))
288 .collect()
289 }
290}
291
292impl<T> SmartList<T>
293where
294 T: VersionedEntity,
295{
296 pub fn versions(&self) -> Vec<i64> {
297 self.data.iter().map(VersionedEntity::version).collect()
298 }
299}
300
301impl<T> From<Vec<T>> for SmartList<T> {
302 fn from(data: Vec<T>) -> Self {
303 Self::new(data)
304 }
305}
306
307impl<T> Default for SmartList<T> {
308 fn default() -> Self {
309 Self::empty()
310 }
311}
312
313impl<T> IntoIterator for SmartList<T> {
314 type Item = T;
315 type IntoIter = std::vec::IntoIter<T>;
316
317 fn into_iter(self) -> Self::IntoIter {
318 self.data.into_iter()
319 }
320}
321
322impl<'a, T> IntoIterator for &'a SmartList<T> {
323 type Item = &'a T;
324 type IntoIter = std::slice::Iter<'a, T>;
325
326 fn into_iter(self) -> Self::IntoIter {
327 self.data.iter()
328 }
329}
330
331impl<'a, T> IntoIterator for &'a mut SmartList<T> {
332 type Item = &'a mut T;
333 type IntoIter = std::slice::IterMut<'a, T>;
334
335 fn into_iter(self) -> Self::IntoIter {
336 self.data.iter_mut()
337 }
338}
339
340impl<T> FromIterator<T> for SmartList<T> {
341 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
342 Self::new(iter.into_iter().collect())
343 }
344}
345
346impl<T> Extend<T> for SmartList<T> {
347 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
348 self.data.extend(iter);
349 }
350}
351
352impl<T> Index<usize> for SmartList<T> {
353 type Output = T;
354
355 fn index(&self, index: usize) -> &Self::Output {
356 &self.data[index]
357 }
358}
359
360impl<T> IndexMut<usize> for SmartList<T> {
361 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
362 &mut self.data[index]
363 }
364}
365
366impl<T> std::ops::Deref for SmartList<T> {
367 type Target = [T];
368
369 #[inline(always)]
370 fn deref(&self) -> &Self::Target {
371 &self.data
372 }
373}
374
375impl<T> std::ops::DerefMut for SmartList<T> {
376 #[inline(always)]
377 fn deref_mut(&mut self) -> &mut Self::Target {
378 &mut self.data
379 }
380}
381
382impl<T> From<SmartList<T>> for Vec<T> {
383 #[inline(always)]
384 fn from(list: SmartList<T>) -> Self {
385 list.data
386 }
387}
388
389fn id_key(value: &Value) -> String {
390 match value {
391 Value::Null => "null".to_owned(),
392 Value::Bool(value) => format!("b:{value}"),
393 Value::I64(value) => format!("i:{value}"),
394 Value::U64(value) => format!("u:{value}"),
395 Value::F64(value) => format!("f:{value}"),
396 Value::Decimal(value) => format!("decimal:{value}"),
397 Value::Text(value) => format!("t:{value}"),
398 Value::Json(value) => format!("j:{value}"),
399 Value::Date(value) => format!("date:{value}"),
400 Value::Timestamp(value) => format!("ts:{}", value.to_rfc3339()),
401 Value::Object(_) => "object".to_owned(),
402 Value::List(_) => "list".to_owned(),
403 Value::TypedNull(_) => "null".to_owned(),
404 }
405}
406
407#[cfg(test)]
408mod tests {
409 use super::SmartList;
410
411 #[derive(Clone, Debug, PartialEq, Eq)]
412 struct Item {
413 id: u64,
414 value: &'static str,
415 }
416
417 #[test]
418 fn smart_list_merge_by_replaces_in_place_and_appends_new_keys() {
419 let mut items = SmartList::from(vec![
420 Item {
421 id: 1,
422 value: "one",
423 },
424 Item {
425 id: 2,
426 value: "old two",
427 },
428 Item {
429 id: 4,
430 value: "four",
431 },
432 ]);
433
434 items.merge_by(
435 [
436 Item {
437 id: 2,
438 value: "new two",
439 },
440 Item {
441 id: 3,
442 value: "three",
443 },
444 ],
445 |item| item.id,
446 );
447
448 assert_eq!(
449 items.into_vec(),
450 vec![
451 Item {
452 id: 1,
453 value: "one",
454 },
455 Item {
456 id: 2,
457 value: "new two",
458 },
459 Item {
460 id: 4,
461 value: "four",
462 },
463 Item {
464 id: 3,
465 value: "three",
466 },
467 ]
468 );
469 }
470
471 #[test]
472 fn smart_list_merge_by_keeps_one_position_for_repeated_incoming_keys() {
473 let mut items = SmartList::from(vec![Item {
474 id: 1,
475 value: "one",
476 }]);
477
478 items.merge_by(
479 [
480 Item {
481 id: 2,
482 value: "first two",
483 },
484 Item {
485 id: 2,
486 value: "final two",
487 },
488 ],
489 |item| item.id,
490 );
491
492 assert_eq!(
493 items.into_vec(),
494 vec![
495 Item {
496 id: 1,
497 value: "one",
498 },
499 Item {
500 id: 2,
501 value: "final two",
502 },
503 ]
504 );
505 }
506}