rust_queries_builder/lazy.rs
1//! Lazy query implementation using iterators.
2//!
3//! This module provides lazy evaluation of queries, deferring execution
4//! until results are actually consumed.
5
6use key_paths_core::KeyPaths;
7use std::marker::PhantomData;
8
9/// A lazy query builder that uses iterators for deferred execution.
10///
11/// Unlike the standard `Query`, `LazyQuery` doesn't execute until you call
12/// a terminal operation like `.collect()`, `.count()`, or `.first()`.
13///
14/// # Benefits
15///
16/// - **Deferred execution**: No work until results needed
17/// - **Iterator fusion**: Rust optimizes chained operations
18/// - **Early termination**: `.take()` stops as soon as enough items found
19/// - **Composable**: Build complex queries by composition
20///
21/// # Example
22///
23/// ```ignore
24/// // Nothing executes yet
25/// let query = LazyQuery::new(&products)
26/// .where_(Product::price_r(), |&p| p < 100.0)
27/// .where_(Product::stock_r(), |&s| s > 0);
28///
29/// // Execution happens here
30/// let results: Vec<_> = query.collect();
31/// ```
32pub struct LazyQuery<'a, T: 'static, I>
33where
34 I: Iterator<Item = &'a T>,
35{
36 iter: I,
37 _phantom: PhantomData<&'a T>,
38}
39
40impl<'a, T: 'static> LazyQuery<'a, T, std::slice::Iter<'a, T>> {
41 /// Creates a new lazy query from a slice.
42 ///
43 /// # Example
44 ///
45 /// ```ignore
46 /// let query = LazyQuery::new(&products);
47 /// ```
48 pub fn new(data: &'a [T]) -> Self {
49 Self {
50 iter: data.iter(),
51 _phantom: PhantomData,
52 }
53 }
54}
55
56impl<'a, T: 'static, I> LazyQuery<'a, T, I>
57where
58 I: Iterator<Item = &'a T> + 'a,
59{
60 /// Adds a filter predicate (lazy - not executed yet).
61 ///
62 /// # Example
63 ///
64 /// ```ignore
65 /// let query = LazyQuery::new(&products)
66 /// .where_(Product::price_r(), |&p| p < 100.0);
67 /// ```
68 pub fn where_<F, P>(self, path: KeyPaths<T, F>, predicate: P) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
69 where
70 F: 'static,
71 P: Fn(&F) -> bool + 'a,
72 {
73 LazyQuery {
74 iter: self.iter.filter(move |item| {
75 path.get(item).map_or(false, |val| predicate(val))
76 }),
77 _phantom: PhantomData,
78 }
79 }
80
81 /// Maps each item through a transformation (lazy).
82 ///
83 /// # Example
84 ///
85 /// ```ignore
86 /// let prices = LazyQuery::new(&products)
87 /// .map_items(|p| p.price)
88 /// .collect::<Vec<_>>();
89 /// ```
90 pub fn map_items<F, O>(self, f: F) -> impl Iterator<Item = O> + 'a
91 where
92 F: Fn(&'a T) -> O + 'a,
93 I: 'a,
94 {
95 self.iter.map(f)
96 }
97
98 /// Selects/projects a field value (lazy).
99 ///
100 /// Returns iterator over cloned field values.
101 ///
102 /// # Example
103 ///
104 /// ```ignore
105 /// let names: Vec<String> = LazyQuery::new(&products)
106 /// .select_lazy(Product::name_r())
107 /// .collect();
108 /// ```
109 pub fn select_lazy<F>(self, path: KeyPaths<T, F>) -> impl Iterator<Item = F> + 'a
110 where
111 F: Clone + 'static,
112 I: 'a,
113 {
114 self.iter.filter_map(move |item| path.get(item).cloned())
115 }
116
117 /// Takes at most `n` items (lazy).
118 ///
119 /// # Example
120 ///
121 /// ```ignore
122 /// let first_10: Vec<_> = LazyQuery::new(&products)
123 /// .take_lazy(10)
124 /// .collect();
125 /// ```
126 pub fn take_lazy(self, n: usize) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
127 where
128 I: 'a,
129 {
130 LazyQuery {
131 iter: self.iter.take(n),
132 _phantom: PhantomData,
133 }
134 }
135
136 /// Skips `n` items (lazy).
137 ///
138 /// # Example
139 ///
140 /// ```ignore
141 /// let page_2: Vec<_> = LazyQuery::new(&products)
142 /// .skip_lazy(10)
143 /// .take_lazy(10)
144 /// .collect();
145 /// ```
146 pub fn skip_lazy(self, n: usize) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
147 where
148 I: 'a,
149 {
150 LazyQuery {
151 iter: self.iter.skip(n),
152 _phantom: PhantomData,
153 }
154 }
155
156 /// Collects all items into a vector (terminal operation - executes query).
157 ///
158 /// # Example
159 ///
160 /// ```ignore
161 /// let results: Vec<&Product> = query.collect();
162 /// ```
163 pub fn collect(self) -> Vec<&'a T> {
164 self.iter.collect()
165 }
166
167 /// Gets the first item (terminal operation - executes until first match).
168 ///
169 /// # Example
170 ///
171 /// ```ignore
172 /// let first = query.first();
173 /// ```
174 pub fn first(mut self) -> Option<&'a T> {
175 self.iter.next()
176 }
177
178 /// Counts items (terminal operation - executes query).
179 ///
180 /// # Example
181 ///
182 /// ```ignore
183 /// let count = query.count();
184 /// ```
185 pub fn count(self) -> usize {
186 self.iter.count()
187 }
188
189 /// Checks if any items match (terminal operation - short-circuits).
190 ///
191 /// # Example
192 ///
193 /// ```ignore
194 /// let exists = query.any();
195 /// ```
196 pub fn any(mut self) -> bool {
197 self.iter.next().is_some()
198 }
199
200 /// Executes a function for each item (terminal operation).
201 ///
202 /// # Example
203 ///
204 /// ```ignore
205 /// query.for_each(|item| println!("{:?}", item));
206 /// ```
207 pub fn for_each<F>(self, f: F)
208 where
209 F: FnMut(&'a T),
210 {
211 self.iter.for_each(f)
212 }
213
214 /// Folds the iterator (terminal operation).
215 ///
216 /// # Example
217 ///
218 /// ```ignore
219 /// let sum = query.fold(0.0, |acc, item| acc + item.price);
220 /// ```
221 pub fn fold<B, F>(self, init: B, f: F) -> B
222 where
223 F: FnMut(B, &'a T) -> B,
224 {
225 self.iter.fold(init, f)
226 }
227
228 /// Finds an item matching a predicate (terminal - short-circuits).
229 ///
230 /// # Example
231 ///
232 /// ```ignore
233 /// let found = query.find(|item| item.id == 42);
234 /// ```
235 pub fn find<P>(mut self, predicate: P) -> Option<&'a T>
236 where
237 P: FnMut(&&'a T) -> bool,
238 {
239 self.iter.find(predicate)
240 }
241
242 /// Checks if all items match a predicate (terminal - short-circuits).
243 ///
244 /// # Example
245 ///
246 /// ```ignore
247 /// let all_positive = query.all_match(|item| item.value > 0);
248 /// ```
249 pub fn all_match<P>(mut self, mut predicate: P) -> bool
250 where
251 P: FnMut(&'a T) -> bool,
252 {
253 self.iter.all(move |item| predicate(item))
254 }
255
256 /// Converts to a standard iterator for further chaining.
257 ///
258 /// # Example
259 ///
260 /// ```ignore
261 /// let custom: Vec<_> = query
262 /// .into_iter()
263 /// .map(|item| item.custom_transform())
264 /// .filter(|x| x.is_valid())
265 /// .collect();
266 /// ```
267 pub fn into_iter(self) -> I {
268 self.iter
269 }
270}
271
272// Aggregation operations
273impl<'a, T: 'static, I> LazyQuery<'a, T, I>
274where
275 I: Iterator<Item = &'a T> + 'a,
276{
277 /// Computes sum of a field (terminal operation).
278 ///
279 /// # Example
280 ///
281 /// ```ignore
282 /// let total: f64 = LazyQuery::new(&products)
283 /// .sum_by(Product::price_r());
284 /// ```
285 pub fn sum_by<F>(self, path: KeyPaths<T, F>) -> F
286 where
287 F: Clone + std::ops::Add<Output = F> + Default + 'static,
288 I: 'a,
289 {
290 self.iter
291 .filter_map(move |item| path.get(item).cloned())
292 .fold(F::default(), |acc, val| acc + val)
293 }
294
295 /// Computes average of a float field (terminal operation).
296 ///
297 /// # Example
298 ///
299 /// ```ignore
300 /// let avg = LazyQuery::new(&products)
301 /// .avg_by(Product::price_r());
302 /// ```
303 pub fn avg_by(self, path: KeyPaths<T, f64>) -> Option<f64>
304 where
305 I: 'a,
306 {
307 let items: Vec<f64> = self
308 .iter
309 .filter_map(move |item| path.get(item).cloned())
310 .collect();
311
312 if items.is_empty() {
313 None
314 } else {
315 Some(items.iter().sum::<f64>() / items.len() as f64)
316 }
317 }
318
319 /// Finds minimum value of a field (terminal operation).
320 ///
321 /// # Example
322 ///
323 /// ```ignore
324 /// let min = LazyQuery::new(&products)
325 /// .min_by(Product::price_r());
326 /// ```
327 pub fn min_by<F>(self, path: KeyPaths<T, F>) -> Option<F>
328 where
329 F: Ord + Clone + 'static,
330 I: 'a,
331 {
332 self.iter.filter_map(move |item| path.get(item).cloned()).min()
333 }
334
335 /// Finds maximum value of a field (terminal operation).
336 ///
337 /// # Example
338 ///
339 /// ```ignore
340 /// let max = LazyQuery::new(&products)
341 /// .max_by(Product::price_r());
342 /// ```
343 pub fn max_by<F>(self, path: KeyPaths<T, F>) -> Option<F>
344 where
345 F: Ord + Clone + 'static,
346 I: 'a,
347 {
348 self.iter.filter_map(move |item| path.get(item).cloned()).max()
349 }
350
351 /// Finds minimum float value (terminal operation).
352 pub fn min_by_float(self, path: KeyPaths<T, f64>) -> Option<f64>
353 where
354 I: 'a,
355 {
356 self.iter
357 .filter_map(move |item| path.get(item).cloned())
358 .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
359 }
360
361 /// Finds maximum float value (terminal operation).
362 pub fn max_by_float(self, path: KeyPaths<T, f64>) -> Option<f64>
363 where
364 I: 'a,
365 {
366 self.iter
367 .filter_map(move |item| path.get(item).cloned())
368 .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
369 }
370}
371
372// Enable using LazyQuery in for loops
373impl<'a, T: 'static, I> IntoIterator for LazyQuery<'a, T, I>
374where
375 I: Iterator<Item = &'a T>,
376{
377 type Item = &'a T;
378 type IntoIter = I;
379
380 fn into_iter(self) -> Self::IntoIter {
381 self.iter
382 }
383}
384