rust_queries_core/lock_lazy.rs
1//! Lazy query support for locked data structures.
2//!
3//! Provides lazy evaluation with early termination for locked collections.
4
5use crate::locks::LockValue;
6use key_paths_core::KeyPaths;
7use std::marker::PhantomData;
8
9/// Lazy query for locked data with early termination.
10pub struct LockLazyQuery<'a, T: 'static, L, I>
11where
12 L: LockValue<T> + 'a,
13 I: Iterator<Item = &'a L>,
14{
15 iter: I,
16 _phantom: PhantomData<&'a T>,
17}
18
19impl<'a, T: 'static, L, I> LockLazyQuery<'a, T, L, I>
20where
21 L: LockValue<T> + 'a,
22 I: Iterator<Item = &'a L> + 'a,
23{
24 /// Create a new lazy query from an iterator of locks.
25 pub fn new(iter: I) -> Self {
26 Self {
27 iter,
28 _phantom: PhantomData,
29 }
30 }
31
32 /// Filter using a key-path predicate (lazy).
33 pub fn where_<F, P>(self, path: KeyPaths<T, F>, predicate: P) -> LockLazyQuery<'a, T, L, impl Iterator<Item = &'a L> + 'a>
34 where
35 F: 'static,
36 P: Fn(&F) -> bool + 'a,
37 {
38 LockLazyQuery {
39 iter: self.iter.filter(move |lock| {
40 lock.with_value(|item| {
41 path.get(item).map_or(false, |val| predicate(val))
42 })
43 .unwrap_or(false)
44 }),
45 _phantom: PhantomData,
46 }
47 }
48
49 /// Map to a field value (lazy).
50 ///
51 /// This allows you to select only specific fields from locked data without
52 /// cloning the entire object. Perfect for projecting data efficiently.
53 ///
54 /// # Example
55 ///
56 /// ```ignore
57 /// // Select only product names (not full objects)
58 /// let names: Vec<String> = products
59 /// .lock_lazy_query()
60 /// .where_(Product::price_r(), |&p| p > 100.0)
61 /// .select_lazy(Product::name_r())
62 /// .collect();
63 ///
64 /// // Select only IDs
65 /// let ids: Vec<u32> = products
66 /// .lock_lazy_query()
67 /// .where_(Product::stock_r(), |&s| s > 0)
68 /// .select_lazy(Product::id_r())
69 /// .take(10)
70 /// .collect();
71 ///
72 /// // Select prices and compute sum
73 /// let total: f64 = products
74 /// .lock_lazy_query()
75 /// .where_(Product::category_r(), |c| c == "Electronics")
76 /// .select_lazy(Product::price_r())
77 /// .sum();
78 /// ```
79 ///
80 /// **Performance Note**: This is much more efficient than collecting full objects
81 /// and then extracting fields, as it only clones the specific field value.
82 pub fn select_lazy<F>(self, path: KeyPaths<T, F>) -> impl Iterator<Item = F> + 'a
83 where
84 F: Clone + 'static,
85 {
86 self.iter.filter_map(move |lock| {
87 lock.with_value(|item| path.get(item).cloned()).flatten()
88 })
89 }
90
91 /// Take first N items (lazy).
92 pub fn take_lazy(self, n: usize) -> impl Iterator<Item = T> + 'a
93 where
94 T: Clone,
95 {
96 self.iter
97 .filter_map(|lock| lock.with_value(|item| item.clone()))
98 .take(n)
99 }
100
101 /// Skip first N items (lazy).
102 pub fn skip_lazy(self, n: usize) -> LockLazyQuery<'a, T, L, impl Iterator<Item = &'a L> + 'a> {
103 LockLazyQuery {
104 iter: self.iter.skip(n),
105 _phantom: PhantomData,
106 }
107 }
108
109 /// Count matching items (terminal).
110 pub fn count(self) -> usize {
111 self.iter.count()
112 }
113
114 /// Get first matching item (terminal).
115 pub fn first(mut self) -> Option<T>
116 where
117 T: Clone,
118 {
119 self.iter
120 .find_map(|lock| lock.with_value(|item| item.clone()))
121 }
122
123 /// Check if any items match (terminal).
124 pub fn any(mut self) -> bool {
125 self.iter.next().is_some()
126 }
127
128 /// Collect into Vec (terminal).
129 pub fn collect(self) -> Vec<T>
130 where
131 T: Clone,
132 {
133 self.iter
134 .filter_map(|lock| lock.with_value(|item| item.clone()))
135 .collect()
136 }
137
138 /// Get all matching items (alias for collect, similar to LockQuery::all).
139 ///
140 /// This provides a familiar API for users coming from LockQuery.
141 ///
142 /// # Example
143 ///
144 /// ```ignore
145 /// let all_items: Vec<Product> = products
146 /// .lock_lazy_query()
147 /// .where_(Product::price_r(), |&p| p > 100.0)
148 /// .all();
149 /// ```
150 pub fn all(self) -> Vec<T>
151 where
152 T: Clone,
153 {
154 self.collect()
155 }
156}
157