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
673pub trait LockQueryable<T, L>
675where
676 L: LockValue<T>,
677{
678 fn lock_query(&self) -> LockQuery<'_, T, L>;
680}
681
682impl<K, V> LockQueryable<V, Arc<RwLock<V>>> for HashMap<K, Arc<RwLock<V>>>
684where
685 K: Eq + std::hash::Hash,
686{
687 fn lock_query(&self) -> LockQuery<'_, V, Arc<RwLock<V>>> {
688 LockQuery::from_locks(self.values().collect())
689 }
690}
691
692impl<K, V> LockQueryable<V, Arc<Mutex<V>>> for HashMap<K, Arc<Mutex<V>>>
694where
695 K: Eq + std::hash::Hash,
696{
697 fn lock_query(&self) -> LockQuery<'_, V, Arc<Mutex<V>>> {
698 LockQuery::from_locks(self.values().collect())
699 }
700}
701
702impl<T> LockQueryable<T, Arc<RwLock<T>>> for Vec<Arc<RwLock<T>>> {
704 fn lock_query(&self) -> LockQuery<'_, T, Arc<RwLock<T>>> {
705 LockQuery::from_locks(self.iter().collect())
706 }
707}
708
709impl<T> LockQueryable<T, Arc<Mutex<T>>> for Vec<Arc<Mutex<T>>> {
711 fn lock_query(&self) -> LockQuery<'_, T, Arc<Mutex<T>>> {
712 LockQuery::from_locks(self.iter().collect())
713 }
714}
715
716use crate::lock_lazy::LockLazyQuery;
718
719pub trait LockLazyQueryable<T, L>
721where
722 L: LockValue<T>,
723{
724 fn lock_lazy_query(&self) -> LockLazyQuery<'_, T, L, impl Iterator<Item = &L>>;
726}
727
728impl<K, V> LockLazyQueryable<V, Arc<RwLock<V>>> for HashMap<K, Arc<RwLock<V>>>
730where
731 K: Eq + std::hash::Hash,
732{
733 fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, Arc<RwLock<V>>, impl Iterator<Item = &Arc<RwLock<V>>>> {
734 LockLazyQuery::new(self.values())
735 }
736}
737
738impl<K, V> LockLazyQueryable<V, Arc<Mutex<V>>> for HashMap<K, Arc<Mutex<V>>>
740where
741 K: Eq + std::hash::Hash,
742{
743 fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, Arc<Mutex<V>>, impl Iterator<Item = &Arc<Mutex<V>>>> {
744 LockLazyQuery::new(self.values())
745 }
746}
747
748impl<T> LockLazyQueryable<T, Arc<RwLock<T>>> for Vec<Arc<RwLock<T>>> {
750 fn lock_lazy_query(&self) -> LockLazyQuery<'_, T, Arc<RwLock<T>>, impl Iterator<Item = &Arc<RwLock<T>>>> {
751 LockLazyQuery::new(self.iter())
752 }
753}
754
755impl<T> LockLazyQueryable<T, Arc<Mutex<T>>> for Vec<Arc<Mutex<T>>> {
757 fn lock_lazy_query(&self) -> LockLazyQuery<'_, T, Arc<Mutex<T>>, impl Iterator<Item = &Arc<Mutex<T>>>> {
758 LockLazyQuery::new(self.iter())
759 }
760}
761
762#[cfg(test)]
763mod tests {
764 use super::*;
765 use std::sync::{Arc, RwLock};
766 use key_paths_derive::Keypath;
767
768 #[derive(Clone, Keypath)]
769 struct Product {
770 id: u32,
771 name: String,
772 price: f64,
773 category: String,
774 }
775
776 fn create_test_map() -> HashMap<String, Arc<RwLock<Product>>> {
777 let mut map = HashMap::new();
778 map.insert(
779 "p1".to_string(),
780 Arc::new(RwLock::new(Product {
781 id: 1,
782 name: "Laptop".to_string(),
783 price: 999.99,
784 category: "Electronics".to_string(),
785 })),
786 );
787 map.insert(
788 "p2".to_string(),
789 Arc::new(RwLock::new(Product {
790 id: 2,
791 name: "Chair".to_string(),
792 price: 299.99,
793 category: "Furniture".to_string(),
794 })),
795 );
796 map.insert(
797 "p3".to_string(),
798 Arc::new(RwLock::new(Product {
799 id: 3,
800 name: "Mouse".to_string(),
801 price: 29.99,
802 category: "Electronics".to_string(),
803 })),
804 );
805 map
806 }
807
808 #[test]
809 fn test_lock_query_where() {
810 let map = create_test_map();
811 let query = map.lock_query();
812 let count = query
813 .where_(Product::category(), |cat| cat == "Electronics")
814 .count();
815 assert_eq!(count, 2);
816 }
817
818 #[test]
819 fn test_lock_query_select() {
820 let map = create_test_map();
821 let names = map
822 .lock_query()
823 .select(Product::name());
824 assert_eq!(names.len(), 3);
825 }
826
827 #[test]
828 fn test_lock_query_sum() {
829 let map = create_test_map();
830 let total = map
831 .lock_query()
832 .sum(Product::price());
833 assert!((total - 1329.97).abs() < 0.01);
834 }
835
836 #[test]
837 fn test_lock_query_group_by() {
838 let map = create_test_map();
839 let groups = map
840 .lock_query()
841 .group_by(Product::category());
842 assert_eq!(groups.len(), 2);
843 assert_eq!(groups.get("Electronics").unwrap().len(), 2);
844 }
845
846 #[test]
847 fn test_lock_query_order_by() {
848 let map = create_test_map();
849 let sorted = map
850 .lock_query()
851 .order_by_float(Product::price());
852 assert_eq!(sorted[0].price, 29.99);
853 assert_eq!(sorted[2].price, 999.99);
854 }
855}
856