1use crate::locks::LockValue;
25use key_paths_core::KeyPaths;
26use std::collections::HashMap;
27use std::sync::{Arc, RwLock, Mutex};
28
29pub struct LockQuery<'a, T: 'static, L>
34where
35 L: LockValue<T> + 'a,
36{
37 locks: Vec<&'a L>,
38 filters: Vec<Box<dyn Fn(&T) -> bool + 'a>>,
39 _phantom: std::marker::PhantomData<T>,
40}
41
42impl<'a, T: 'static, L> LockQuery<'a, T, L>
43where
44 L: LockValue<T> + 'a,
45{
46 pub fn from_locks(locks: Vec<&'a L>) -> Self {
54 Self {
55 locks,
56 filters: Vec::new(),
57 _phantom: std::marker::PhantomData,
58 }
59 }
60
61 pub fn where_<F>(mut self, path: KeyPaths<T, F>, predicate: impl Fn(&F) -> bool + 'a) -> Self
70 where
71 F: 'static,
72 {
73 self.filters.push(Box::new(move |item| {
74 path.get(item).map_or(false, |val| predicate(val))
75 }));
76 self
77 }
78
79 pub fn all(&self) -> Vec<T>
87 where
88 T: Clone,
89 {
90 self.locks
91 .iter()
92 .filter_map(|lock| {
93 lock.with_value(|item| {
94 if self.filters.iter().all(|f| f(item)) {
95 Some(item.clone())
96 } else {
97 None
98 }
99 })
100 .flatten()
101 })
102 .collect()
103 }
104
105 pub fn first(&self) -> Option<T>
113 where
114 T: Clone,
115 {
116 self.locks
117 .iter()
118 .find_map(|lock| {
119 lock.with_value(|item| {
120 if self.filters.iter().all(|f| f(item)) {
121 Some(item.clone())
122 } else {
123 None
124 }
125 })
126 .flatten()
127 })
128 }
129
130 pub fn count(&self) -> usize {
138 self.locks
139 .iter()
140 .filter(|lock| {
141 lock.with_value(|item| self.filters.iter().all(|f| f(item)))
142 .unwrap_or(false)
143 })
144 .count()
145 }
146
147 pub fn exists(&self) -> bool {
155 self.locks
156 .iter()
157 .any(|lock| {
158 lock.with_value(|item| self.filters.iter().all(|f| f(item)))
159 .unwrap_or(false)
160 })
161 }
162
163 pub fn limit(&self, n: usize) -> Vec<T>
171 where
172 T: Clone,
173 {
174 self.locks
175 .iter()
176 .filter_map(|lock| {
177 lock.with_value(|item| {
178 if self.filters.iter().all(|f| f(item)) {
179 Some(item.clone())
180 } else {
181 None
182 }
183 })
184 .flatten()
185 })
186 .take(n)
187 .collect()
188 }
189
190 pub fn select<F>(&self, path: KeyPaths<T, F>) -> Vec<F>
198 where
199 F: Clone + 'static,
200 {
201 self.locks
202 .iter()
203 .filter_map(|lock| {
204 lock.with_value(|item| {
205 if self.filters.iter().all(|f| f(item)) {
206 path.get(item).cloned()
207 } else {
208 None
209 }
210 })
211 .flatten()
212 })
213 .collect()
214 }
215
216 pub fn sum<F>(&self, path: KeyPaths<T, F>) -> F
224 where
225 F: Clone + std::ops::Add<Output = F> + Default + 'static,
226 {
227 self.locks
228 .iter()
229 .filter_map(|lock| {
230 lock.with_value(|item| {
231 if self.filters.iter().all(|f| f(item)) {
232 path.get(item).cloned()
233 } else {
234 None
235 }
236 })
237 .flatten()
238 })
239 .fold(F::default(), |acc, val| acc + val)
240 }
241
242 pub fn avg(&self, path: KeyPaths<T, f64>) -> Option<f64> {
250 let values: Vec<f64> = self.select(path);
251 if values.is_empty() {
252 None
253 } else {
254 Some(values.iter().sum::<f64>() / values.len() as f64)
255 }
256 }
257
258 pub fn min<F>(&self, path: KeyPaths<T, F>) -> Option<F>
266 where
267 F: Ord + Clone + 'static,
268 {
269 self.select(path).into_iter().min()
270 }
271
272 pub fn max<F>(&self, path: KeyPaths<T, F>) -> Option<F>
280 where
281 F: Ord + Clone + 'static,
282 {
283 self.select(path).into_iter().max()
284 }
285
286 pub fn min_float(&self, path: KeyPaths<T, f64>) -> Option<f64> {
288 self.select(path)
289 .into_iter()
290 .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
291 }
292
293 pub fn max_float(&self, path: KeyPaths<T, f64>) -> Option<f64> {
295 self.select(path)
296 .into_iter()
297 .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
298 }
299
300 pub fn order_by<F>(&self, path: KeyPaths<T, F>) -> Vec<T>
308 where
309 F: Ord + Clone + 'static,
310 T: Clone,
311 {
312 let mut results = self.all();
313 results.sort_by_key(|item| path.get(item).cloned());
314 results
315 }
316
317 pub fn order_by_desc<F>(&self, path: KeyPaths<T, F>) -> Vec<T>
319 where
320 F: Ord + Clone + 'static,
321 T: Clone,
322 {
323 let mut results = self.all();
324 results.sort_by(|a, b| {
325 let a_val = path.get(a).cloned();
326 let b_val = path.get(b).cloned();
327 b_val.cmp(&a_val)
328 });
329 results
330 }
331
332 pub fn order_by_float(&self, path: KeyPaths<T, f64>) -> Vec<T>
334 where
335 T: Clone,
336 {
337 let mut results = self.all();
338 results.sort_by(|a, b| {
339 let a_val = path.get(a).cloned().unwrap_or(0.0);
340 let b_val = path.get(b).cloned().unwrap_or(0.0);
341 a_val.partial_cmp(&b_val).unwrap_or(std::cmp::Ordering::Equal)
342 });
343 results
344 }
345
346 pub fn order_by_float_desc(&self, path: KeyPaths<T, f64>) -> Vec<T>
348 where
349 T: Clone,
350 {
351 let mut results = self.all();
352 results.sort_by(|a, b| {
353 let a_val = path.get(a).cloned().unwrap_or(0.0);
354 let b_val = path.get(b).cloned().unwrap_or(0.0);
355 b_val.partial_cmp(&a_val).unwrap_or(std::cmp::Ordering::Equal)
356 });
357 results
358 }
359
360 pub fn group_by<F>(&self, path: KeyPaths<T, F>) -> HashMap<F, Vec<T>>
368 where
369 F: Eq + std::hash::Hash + Clone + 'static,
370 T: Clone,
371 {
372 let mut groups: HashMap<F, Vec<T>> = HashMap::new();
373
374 for lock in &self.locks {
375 if let Some(item) = lock.with_value(|item| {
376 if self.filters.iter().all(|f| f(item)) {
377 Some(item.clone())
378 } else {
379 None
380 }
381 })
382 .flatten()
383 {
384 if let Some(key) = path.get(&item).cloned() {
385 groups.entry(key).or_insert_with(Vec::new).push(item);
386 }
387 }
388 }
389
390 groups
391 }
392
393 pub fn min_timestamp(&self, path: KeyPaths<T, i64>) -> Option<i64> {
402 self.locks
403 .iter()
404 .filter_map(|lock| {
405 lock.with_value(|item| {
406 if self.filters.iter().all(|f| f(item)) {
407 path.get(item).cloned()
408 } else {
409 None
410 }
411 })
412 .flatten()
413 })
414 .min()
415 }
416
417 pub fn max_timestamp(&self, path: KeyPaths<T, i64>) -> Option<i64> {
425 self.locks
426 .iter()
427 .filter_map(|lock| {
428 lock.with_value(|item| {
429 if self.filters.iter().all(|f| f(item)) {
430 path.get(item).cloned()
431 } else {
432 None
433 }
434 })
435 .flatten()
436 })
437 .max()
438 }
439
440 pub fn avg_timestamp(&self, path: KeyPaths<T, i64>) -> Option<i64> {
448 let items: Vec<i64> = self.locks
449 .iter()
450 .filter_map(|lock| {
451 lock.with_value(|item| {
452 if self.filters.iter().all(|f| f(item)) {
453 path.get(item).cloned()
454 } else {
455 None
456 }
457 })
458 .flatten()
459 })
460 .collect();
461
462 if items.is_empty() {
463 None
464 } else {
465 Some(items.iter().sum::<i64>() / items.len() as i64)
466 }
467 }
468
469 pub fn sum_timestamp(&self, path: KeyPaths<T, i64>) -> i64 {
477 self.locks
478 .iter()
479 .filter_map(|lock| {
480 lock.with_value(|item| {
481 if self.filters.iter().all(|f| f(item)) {
482 path.get(item).cloned()
483 } else {
484 None
485 }
486 })
487 .flatten()
488 })
489 .sum()
490 }
491
492 pub fn count_timestamp(&self, path: KeyPaths<T, i64>) -> usize {
500 self.locks
501 .iter()
502 .filter(|lock| {
503 lock.with_value(|item| {
504 if self.filters.iter().all(|f| f(item)) {
505 path.get(item).is_some()
506 } else {
507 false
508 }
509 })
510 .unwrap_or(false)
511 })
512 .count()
513 }
514
515 pub fn where_after_timestamp(self, path: KeyPaths<T, i64>, reference: i64) -> Self {
528 self.where_(path, move |timestamp| timestamp > &reference)
529 }
530
531 pub fn where_before_timestamp(self, path: KeyPaths<T, i64>, reference: i64) -> Self {
544 self.where_(path, move |timestamp| timestamp < &reference)
545 }
546
547 pub fn where_between_timestamp(self, path: KeyPaths<T, i64>, start: i64, end: i64) -> Self {
561 self.where_(path, move |timestamp| timestamp >= &start && timestamp <= &end)
562 }
563
564 pub fn where_last_days_timestamp(self, path: KeyPaths<T, i64>, days: i64) -> Self {
577 let now = chrono::Utc::now().timestamp_millis();
578 let cutoff = now - (days * 24 * 60 * 60 * 1000); self.where_after_timestamp(path, cutoff)
580 }
581
582 pub fn where_next_days_timestamp(self, path: KeyPaths<T, i64>, days: i64) -> Self {
595 let now = chrono::Utc::now().timestamp_millis();
596 let cutoff = now + (days * 24 * 60 * 60 * 1000); self.where_before_timestamp(path, cutoff)
598 }
599
600 pub fn where_last_hours_timestamp(self, path: KeyPaths<T, i64>, hours: i64) -> Self {
613 let now = chrono::Utc::now().timestamp_millis();
614 let cutoff = now - (hours * 60 * 60 * 1000); self.where_after_timestamp(path, cutoff)
616 }
617
618 pub fn where_next_hours_timestamp(self, path: KeyPaths<T, i64>, hours: i64) -> Self {
631 let now = chrono::Utc::now().timestamp_millis();
632 let cutoff = now + (hours * 60 * 60 * 1000); self.where_before_timestamp(path, cutoff)
634 }
635
636 pub fn where_last_minutes_timestamp(self, path: KeyPaths<T, i64>, minutes: i64) -> Self {
649 let now = chrono::Utc::now().timestamp_millis();
650 let cutoff = now - (minutes * 60 * 1000); self.where_after_timestamp(path, cutoff)
652 }
653
654 pub fn where_next_minutes_timestamp(self, path: KeyPaths<T, i64>, minutes: i64) -> Self {
667 let now = chrono::Utc::now().timestamp_millis();
668 let cutoff = now + (minutes * 60 * 1000); self.where_before_timestamp(path, cutoff)
670 }
671}
672
673
674pub trait LockQueryable<T, L>
676where
677 L: LockValue<T>,
678{
679 fn lock_query(&self) -> LockQuery<'_, T, L>;
681}
682
683impl<K, V> LockQueryable<V, Arc<RwLock<V>>> for HashMap<K, Arc<RwLock<V>>>
685where
686 K: Eq + std::hash::Hash,
687{
688 fn lock_query(&self) -> LockQuery<'_, V, Arc<RwLock<V>>> {
689 LockQuery::from_locks(self.values().collect())
690 }
691}
692
693impl<K, V> LockQueryable<V, Arc<Mutex<V>>> for HashMap<K, Arc<Mutex<V>>>
695where
696 K: Eq + std::hash::Hash,
697{
698 fn lock_query(&self) -> LockQuery<'_, V, Arc<Mutex<V>>> {
699 LockQuery::from_locks(self.values().collect())
700 }
701}
702
703impl<T> LockQueryable<T, Arc<RwLock<T>>> for Vec<Arc<RwLock<T>>> {
705 fn lock_query(&self) -> LockQuery<'_, T, Arc<RwLock<T>>> {
706 LockQuery::from_locks(self.iter().collect())
707 }
708}
709
710impl<T> LockQueryable<T, Arc<Mutex<T>>> for Vec<Arc<Mutex<T>>> {
712 fn lock_query(&self) -> LockQuery<'_, T, Arc<Mutex<T>>> {
713 LockQuery::from_locks(self.iter().collect())
714 }
715}
716
717use crate::lock_lazy::LockLazyQuery;
719
720pub trait LockLazyQueryable<T, L>
722where
723 L: LockValue<T>,
724{
725 fn lock_lazy_query(&self) -> LockLazyQuery<'_, T, L, impl Iterator<Item = &L>>;
727}
728
729impl<K, V> LockLazyQueryable<V, Arc<RwLock<V>>> for HashMap<K, Arc<RwLock<V>>>
731where
732 K: Eq + std::hash::Hash,
733{
734 fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, Arc<RwLock<V>>, impl Iterator<Item = &Arc<RwLock<V>>>> {
735 LockLazyQuery::new(self.values())
736 }
737}
738
739impl<K, V> LockLazyQueryable<V, Arc<Mutex<V>>> for HashMap<K, Arc<Mutex<V>>>
741where
742 K: Eq + std::hash::Hash,
743{
744 fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, Arc<Mutex<V>>, impl Iterator<Item = &Arc<Mutex<V>>>> {
745 LockLazyQuery::new(self.values())
746 }
747}
748
749impl<T> LockLazyQueryable<T, Arc<RwLock<T>>> for Vec<Arc<RwLock<T>>> {
751 fn lock_lazy_query(&self) -> LockLazyQuery<'_, T, Arc<RwLock<T>>, impl Iterator<Item = &Arc<RwLock<T>>>> {
752 LockLazyQuery::new(self.iter())
753 }
754}
755
756impl<T> LockLazyQueryable<T, Arc<Mutex<T>>> for Vec<Arc<Mutex<T>>> {
758 fn lock_lazy_query(&self) -> LockLazyQuery<'_, T, Arc<Mutex<T>>, impl Iterator<Item = &Arc<Mutex<T>>>> {
759 LockLazyQuery::new(self.iter())
760 }
761}
762
763#[cfg(test)]
764mod tests {
765 use super::*;
766 use std::sync::{Arc, RwLock};
767 use key_paths_derive::Keypath;
768
769 #[derive(Clone, Keypath)]
770 struct Product {
771 id: u32,
772 name: String,
773 price: f64,
774 category: String,
775 }
776
777 fn create_test_map() -> HashMap<String, Arc<RwLock<Product>>> {
778 let mut map = HashMap::new();
779 map.insert(
780 "p1".to_string(),
781 Arc::new(RwLock::new(Product {
782 id: 1,
783 name: "Laptop".to_string(),
784 price: 999.99,
785 category: "Electronics".to_string(),
786 })),
787 );
788 map.insert(
789 "p2".to_string(),
790 Arc::new(RwLock::new(Product {
791 id: 2,
792 name: "Chair".to_string(),
793 price: 299.99,
794 category: "Furniture".to_string(),
795 })),
796 );
797 map.insert(
798 "p3".to_string(),
799 Arc::new(RwLock::new(Product {
800 id: 3,
801 name: "Mouse".to_string(),
802 price: 29.99,
803 category: "Electronics".to_string(),
804 })),
805 );
806 map
807 }
808
809 #[test]
810 fn test_lock_query_where() {
811 let map = create_test_map();
812 let query = map.lock_query();
813 let count = query
814 .where_(Product::category(), |cat| cat == "Electronics")
815 .count();
816 assert_eq!(count, 2);
817 }
818
819 #[test]
820 fn test_lock_query_select() {
821 let map = create_test_map();
822 let names = map
823 .lock_query()
824 .select(Product::name());
825 assert_eq!(names.len(), 3);
826 }
827
828 #[test]
829 fn test_lock_query_sum() {
830 let map = create_test_map();
831 let total = map
832 .lock_query()
833 .sum(Product::price());
834 assert!((total - 1329.97).abs() < 0.01);
835 }
836
837 #[test]
838 fn test_lock_query_group_by() {
839 let map = create_test_map();
840 let groups = map
841 .lock_query()
842 .group_by(Product::category());
843 assert_eq!(groups.len(), 2);
844 assert_eq!(groups.get("Electronics").unwrap().len(), 2);
845 }
846
847 #[test]
848 fn test_lock_query_order_by() {
849 let map = create_test_map();
850 let sorted = map
851 .lock_query()
852 .order_by_float(Product::price());
853 assert_eq!(sorted[0].price, 29.99);
854 assert_eq!(sorted[2].price, 999.99);
855 }
856}
857