rust_queries_core/
ext.rs

1use crate::{Query, LazyQuery, Queryable};
2
3/// Extension trait that adds eager Query methods directly to slice-like containers
4/// 
5/// This trait allows you to call eager query methods directly on containers.
6/// For lazy queries, use the `QueryableExt::lazy_query()` method instead.
7/// 
8/// ```ignore
9/// use rust_queries_builder::{QueryExt, QueryableExt};
10/// 
11/// let products = vec![...];
12/// 
13/// // Eager query (collects immediately)
14/// let results = products.query().where_(...).all();
15/// 
16/// // Lazy query (deferred execution)
17/// let results = products.lazy_query().where_(...).collect();
18/// ```
19pub trait QueryExt<T> {
20    /// Create an eager Query from this container
21    /// 
22    /// This creates a Query that executes operations immediately when terminal
23    /// methods like `all()`, `first()`, or `count()` are called.
24    fn query(&self) -> Query<T>;
25}
26
27// Implementations for Vec
28impl<T: 'static> QueryExt<T> for Vec<T> {
29    fn query(&self) -> Query<T> {
30        Query::new(self)
31    }
32}
33
34// Implementations for slices
35impl<T: 'static> QueryExt<T> for [T] {
36    fn query(&self) -> Query<T> {
37        Query::new(self)
38    }
39}
40
41// Array implementations
42impl<T: 'static, const N: usize> QueryExt<T> for [T; N] {
43    fn query(&self) -> Query<T> {
44        Query::new(&self[..])
45    }
46}
47
48/// Extension trait for Queryable types that provides query building capabilities.
49/// 
50/// This trait extends any type implementing `Queryable<T>` with methods to create
51/// `LazyQuery` instances directly from the container's iterator.
52/// 
53/// # Example
54/// 
55/// ```ignore
56/// use rust_queries_builder::QueryableExt;
57/// 
58/// let map: HashMap<String, Product> = ...;
59/// let results: Vec<_> = map.lazy_query()
60///     .where_(Product::price(), |&p| p > 100.0)
61///     .collect();
62/// ```
63pub trait QueryableExt<T> {
64    /// Create a lazy query from this Queryable container
65    /// 
66    /// This returns a `LazyQuery` that can be used with all lazy query operations.
67    /// The operations are executed lazily when a terminal operation is called.
68    fn lazy_query(&self) -> LazyQuery<T, Box<dyn Iterator<Item = &T> + '_>>;
69}
70
71// Explicit implementation for slices (which are not Sized)
72impl<T: 'static> QueryableExt<T> for [T] {
73    fn lazy_query(&self) -> LazyQuery<T, Box<dyn Iterator<Item = &T> + '_>> {
74        LazyQuery::from_iter(Box::new(self.iter()))
75    }
76}
77
78// Blanket implementation for all Queryable types
79impl<T: 'static, Q> QueryableExt<T> for Q
80where
81    Q: Queryable<T> + Sized,
82{
83    fn lazy_query(&self) -> LazyQuery<T, Box<dyn Iterator<Item = &T> + '_>> {
84        LazyQuery::from_iter(self.query_iter())
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use key_paths_derive::Keypath;
92
93    #[derive(Debug, Clone, PartialEq, Keypath)]
94    struct Product {
95        id: u32,
96        name: String,
97        price: f64,
98        category: String,
99    }
100
101    #[test]
102    fn test_vec_query_ext() {
103        let products = vec![
104            Product {
105                id: 1,
106                name: "Laptop".to_string(),
107                price: 999.99,
108                category: "Electronics".to_string(),
109            },
110            Product {
111                id: 2,
112                name: "Mouse".to_string(),
113                price: 29.99,
114                category: "Electronics".to_string(),
115            },
116        ];
117
118        let query = products
119            .query()
120            .where_(Product::price(), |&p| p > 50.0);
121        let results = query.all();
122
123        assert_eq!(results.len(), 1);
124        assert_eq!(results[0].name, "Laptop");
125    }
126
127    #[test]
128    fn test_vec_lazy_query_ext() {
129        let products = vec![
130            Product {
131                id: 1,
132                name: "Laptop".to_string(),
133                price: 999.99,
134                category: "Electronics".to_string(),
135            },
136            Product {
137                id: 2,
138                name: "Mouse".to_string(),
139                price: 29.99,
140                category: "Electronics".to_string(),
141            },
142        ];
143
144        let results: Vec<_> = products
145            .lazy_query()
146            .where_(Product::price(), |&p| p > 50.0)
147            .collect();
148
149        assert_eq!(results.len(), 1);
150        assert_eq!(results[0].name, "Laptop");
151    }
152
153    #[test]
154    fn test_array_query_ext() {
155        let products = [
156            Product {
157                id: 1,
158                name: "Laptop".to_string(),
159                price: 999.99,
160                category: "Electronics".to_string(),
161            },
162            Product {
163                id: 2,
164                name: "Mouse".to_string(),
165                price: 29.99,
166                category: "Electronics".to_string(),
167            },
168        ];
169
170        let results: Vec<_> = products
171            .lazy_query()
172            .where_(Product::price(), |&p| p < 100.0)
173            .collect();
174
175        assert_eq!(results.len(), 1);
176        assert_eq!(results[0].name, "Mouse");
177    }
178
179    #[test]
180    fn test_slice_query_ext() {
181        let products = vec![
182            Product {
183                id: 1,
184                name: "Laptop".to_string(),
185                price: 999.99,
186                category: "Electronics".to_string(),
187            },
188            Product {
189                id: 2,
190                name: "Mouse".to_string(),
191                price: 29.99,
192                category: "Electronics".to_string(),
193            },
194        ];
195
196        let slice = &products[..];
197        let query = slice
198            .query()
199            .where_(Product::category(), |cat| cat == "Electronics");
200        let results = query.count();
201
202        assert_eq!(results, 2);
203    }
204
205    #[test]
206    fn test_hashmap_queryable_ext() {
207        use std::collections::HashMap;
208
209        let mut map: HashMap<u32, Product> = HashMap::new();
210        map.insert(
211            1,
212            Product {
213                id: 1,
214                name: "Laptop".to_string(),
215                price: 999.99,
216                category: "Electronics".to_string(),
217            },
218        );
219        map.insert(
220            2,
221            Product {
222                id: 2,
223                name: "Mouse".to_string(),
224                price: 29.99,
225                category: "Electronics".to_string(),
226            },
227        );
228
229        // Using QueryableExt for HashMap
230        let results: Vec<_> = map
231            .lazy_query()
232            .where_(Product::price(), |&p| p > 50.0)
233            .collect();
234
235        assert_eq!(results.len(), 1);
236        assert_eq!(results[0].name, "Laptop");
237    }
238
239    #[test]
240    fn test_btreemap_queryable_ext() {
241        use std::collections::BTreeMap;
242
243        let mut map: BTreeMap<u32, Product> = BTreeMap::new();
244        map.insert(
245            1,
246            Product {
247                id: 1,
248                name: "Laptop".to_string(),
249                price: 999.99,
250                category: "Electronics".to_string(),
251            },
252        );
253        map.insert(
254            2,
255            Product {
256                id: 2,
257                name: "Mouse".to_string(),
258                price: 29.99,
259                category: "Electronics".to_string(),
260            },
261        );
262
263        // Using QueryableExt for BTreeMap
264        let count = map
265            .lazy_query()
266            .where_(Product::category(), |cat| cat == "Electronics")
267            .count();
268
269        assert_eq!(count, 2);
270    }
271
272    #[test]
273    fn test_vecdeque_queryable_ext() {
274        use std::collections::VecDeque;
275
276        let mut deque: VecDeque<Product> = VecDeque::new();
277        deque.push_back(Product {
278            id: 1,
279            name: "Laptop".to_string(),
280            price: 999.99,
281            category: "Electronics".to_string(),
282        });
283        deque.push_back(Product {
284            id: 2,
285            name: "Mouse".to_string(),
286            price: 29.99,
287            category: "Electronics".to_string(),
288        });
289
290        // Using QueryableExt for VecDeque
291        let results: Vec<_> = deque
292            .lazy_query()
293            .where_(Product::price(), |&p| p < 100.0)
294            .collect();
295
296        assert_eq!(results.len(), 1);
297        assert_eq!(results[0].name, "Mouse");
298    }
299
300    #[test]
301    fn test_linkedlist_queryable_ext() {
302        use std::collections::LinkedList;
303
304        let mut list: LinkedList<Product> = LinkedList::new();
305        list.push_back(Product {
306            id: 1,
307            name: "Laptop".to_string(),
308            price: 999.99,
309            category: "Electronics".to_string(),
310        });
311        list.push_back(Product {
312            id: 2,
313            name: "Mouse".to_string(),
314            price: 29.99,
315            category: "Electronics".to_string(),
316        });
317
318        // Using QueryableExt for LinkedList
319        let first = list
320            .lazy_query()
321            .where_(Product::price(), |&p| p > 900.0)
322            .first();
323
324        assert!(first.is_some());
325        assert_eq!(first.unwrap().name, "Laptop");
326    }
327
328    #[test]
329    fn test_queryable_ext_aggregations() {
330        use std::collections::VecDeque;
331
332        let mut deque: VecDeque<Product> = VecDeque::new();
333        deque.push_back(Product {
334            id: 1,
335            name: "Laptop".to_string(),
336            price: 999.99,
337            category: "Electronics".to_string(),
338        });
339        deque.push_back(Product {
340            id: 2,
341            name: "Mouse".to_string(),
342            price: 29.99,
343            category: "Electronics".to_string(),
344        });
345        deque.push_back(Product {
346            id: 3,
347            name: "Keyboard".to_string(),
348            price: 79.99,
349            category: "Electronics".to_string(),
350        });
351
352        // Test aggregation operations
353        let total = deque.lazy_query().sum_by(Product::price());
354        assert!((total - 1109.97).abs() < 0.01);
355
356        let avg = deque.lazy_query().avg_by(Product::price()).unwrap();
357        assert!((avg - 369.99).abs() < 0.01);
358
359        let min = deque.lazy_query().min_by_float(Product::price()).unwrap();
360        assert!((min - 29.99).abs() < 0.01);
361
362        let max = deque.lazy_query().max_by_float(Product::price()).unwrap();
363        assert!((max - 999.99).abs() < 0.01);
364    }
365
366    #[test]
367    fn test_queryable_ext_chaining() {
368        use std::collections::BTreeMap;
369
370        let mut map: BTreeMap<u32, Product> = BTreeMap::new();
371        map.insert(
372            1,
373            Product {
374                id: 1,
375                name: "Laptop".to_string(),
376                price: 999.99,
377                category: "Electronics".to_string(),
378            },
379        );
380        map.insert(
381            2,
382            Product {
383                id: 2,
384                name: "Mouse".to_string(),
385                price: 29.99,
386                category: "Electronics".to_string(),
387            },
388        );
389        map.insert(
390            3,
391            Product {
392                id: 3,
393                name: "Desk".to_string(),
394                price: 299.99,
395                category: "Furniture".to_string(),
396            },
397        );
398
399        // Test complex chaining with multiple where clauses
400        let results: Vec<_> = map
401            .lazy_query()
402            .where_(Product::category(), |cat| cat == "Electronics")
403            .where_(Product::price(), |&p| p < 500.0)
404            .skip_lazy(0)
405            .take_lazy(10)
406            .collect();
407
408        assert_eq!(results.len(), 1);
409        assert_eq!(results[0].name, "Mouse");
410    }
411}