1use rust_key_paths::KpType;
6
7#[cfg(feature = "rayon")]
8pub mod query_par;
9#[cfg(feature = "rayon")]
10pub mod rayon_optimizations;
11#[cfg(feature = "rayon")]
12pub mod scale_par;
13
14pub struct CollectionQuery<'a, Root, Item> {
16 keypath: &'a KpType<'a, Root, Vec<Item>>,
17 filters: Vec<Box<dyn Fn(&Item) -> bool + 'a>>,
18 limit: Option<usize>,
19 offset: usize,
20}
21
22impl<'a, Root, Item> CollectionQuery<'a, Root, Item> {
23 pub fn new(keypath: &'a KpType<'a, Root, Vec<Item>>) -> Self {
24 Self {
25 keypath,
26 filters: Vec::new(),
27 limit: None,
28 offset: 0,
29 }
30 }
31
32 pub fn filter<F>(mut self, predicate: F) -> Self
33 where
34 F: Fn(&Item) -> bool + 'a,
35 {
36 self.filters.push(Box::new(predicate));
37 self
38 }
39
40 pub fn limit(mut self, n: usize) -> Self {
41 self.limit = Some(n);
42 self
43 }
44
45 pub fn offset(mut self, n: usize) -> Self {
46 self.offset = n;
47 self
48 }
49
50 pub fn execute(&self, root: &'a Root) -> Vec<&'a Item> {
51 if let Some(vec) = self.keypath.get(root) {
52 let mut result: Vec<&'a Item> = vec
53 .iter()
54 .skip(self.offset)
55 .filter(|item| self.filters.iter().all(|f| f(item)))
56 .collect();
57
58 if let Some(limit) = self.limit {
59 result.truncate(limit);
60 }
61
62 result
63 } else {
64 Vec::new()
65 }
66 }
67
68 pub fn count(&self, root: &'a Root) -> usize {
69 if let Some(vec) = self.keypath.get(root) {
70 vec.iter()
71 .skip(self.offset)
72 .filter(|item| self.filters.iter().all(|f| f(item)))
73 .take(self.limit.unwrap_or(usize::MAX))
74 .count()
75 } else {
76 0
77 }
78 }
79
80 pub fn exists(&self, root: &'a Root) -> bool {
81 self.count(root) > 0
82 }
83
84 pub fn first(&self, root: &'a Root) -> Option<&'a Item> {
85 self.execute(root).into_iter().next()
86 }
87}
88
89pub trait QueryableCollection<'a, Root, Item> {
92 fn query(&'a self) -> CollectionQuery<'a, Root, Item>;
93}
94
95impl<'a, Root, Item> QueryableCollection<'a, Root, Item> for KpType<'a, Root, Vec<Item>> {
96 fn query(&'a self) -> CollectionQuery<'a, Root, Item> {
97 CollectionQuery::new(self)
98 }
99}
100
101pub struct CollectionQueryStatic<'q, Root, Item>
106where
107 Root: 'static,
108 Item: 'static,
109{
110 keypath: &'q KpType<'static, Root, Vec<Item>>,
111 filters: Vec<Box<dyn Fn(&Item) -> bool + 'q>>,
112 limit: Option<usize>,
113 offset: usize,
114}
115
116impl<'q, Root: 'static, Item: 'static> CollectionQueryStatic<'q, Root, Item> {
117 pub fn new(keypath: &'q KpType<'static, Root, Vec<Item>>) -> Self {
118 Self {
119 keypath,
120 filters: Vec::new(),
121 limit: None,
122 offset: 0,
123 }
124 }
125
126 pub fn filter<F>(mut self, predicate: F) -> Self
127 where
128 F: Fn(&Item) -> bool + 'q,
129 {
130 self.filters.push(Box::new(predicate));
131 self
132 }
133
134 pub fn limit(mut self, n: usize) -> Self {
135 self.limit = Some(n);
136 self
137 }
138
139 pub fn offset(mut self, n: usize) -> Self {
140 self.offset = n;
141 self
142 }
143
144 pub fn execute<'a>(&self, root: &'a Root) -> Vec<&'a Item> {
145 if let Some(vec) = get_vec_static(self.keypath, root) {
146 let mut result: Vec<&'a Item> = vec
147 .iter()
148 .skip(self.offset)
149 .filter(|item| self.filters.iter().all(|f| f(item)))
150 .collect();
151 if let Some(limit) = self.limit {
152 result.truncate(limit);
153 }
154 result
155 } else {
156 Vec::new()
157 }
158 }
159
160 pub fn count<'a>(&self, root: &'a Root) -> usize {
161 if let Some(vec) = get_vec_static(self.keypath, root) {
162 vec.iter()
163 .skip(self.offset)
164 .filter(|item| self.filters.iter().all(|f| f(item)))
165 .take(self.limit.unwrap_or(usize::MAX))
166 .count()
167 } else {
168 0
169 }
170 }
171
172 pub fn exists<'a>(&self, root: &'a Root) -> bool {
173 self.count(root) > 0
174 }
175
176 pub fn first<'a>(&self, root: &'a Root) -> Option<&'a Item> {
177 self.execute(root).into_iter().next()
178 }
179}
180
181#[inline]
185pub(crate) fn get_vec_static<'a, Root: 'static, Item: 'static>(
186 keypath: &KpType<'static, Root, Vec<Item>>,
187 root: &'a Root,
188) -> Option<&'a Vec<Item>> {
189 let root_static: &'static Root = unsafe { std::mem::transmute(root) };
192 let opt = keypath.get(root_static);
193 unsafe { std::mem::transmute(opt) }
194}
195
196pub trait QueryableCollectionStatic<Root, Item>
198where
199 Root: 'static,
200 Item: 'static,
201{
202 fn query(&self) -> CollectionQueryStatic<'_, Root, Item>;
203}
204
205impl<Root: 'static, Item: 'static> QueryableCollectionStatic<Root, Item>
206 for KpType<'static, Root, Vec<Item>>
207{
208 fn query(&self) -> CollectionQueryStatic<'_, Root, Item> {
209 CollectionQueryStatic::new(self)
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::{QueryableCollection, *};
216 use rust_key_paths::Kp;
217
218 #[test]
219 fn test_query_dsl() {
220 struct Database {
221 users: Vec<User>,
222 }
223
224 struct User {
225 id: u32,
226 name: String,
227 age: u32,
228 active: bool,
229 }
230
231 let users_kp: KpType<'_, Database, Vec<User>> = Kp::new(
233 |db: &Database| Some(&db.users),
234 |db: &mut Database| Some(&mut db.users),
235 );
236
237 let db = Database {
238 users: vec![
239 User {
240 id: 1,
241 name: "Alice".into(),
242 age: 25,
243 active: true,
244 },
245 User {
246 id: 2,
247 name: "Bob".into(),
248 age: 30,
249 active: false,
250 },
251 User {
252 id: 3,
253 name: "Charlie".into(),
254 age: 35,
255 active: true,
256 },
257 User {
258 id: 4,
259 name: "Diana".into(),
260 age: 28,
261 active: true,
262 },
263 ],
264 };
265
266 let results = QueryableCollection::query(&users_kp)
268 .filter(|u| u.active)
269 .filter(|u| u.age > 26)
270 .limit(2)
271 .execute(&db);
272
273 assert_eq!(results.len(), 2);
274 assert_eq!(results[0].name, "Charlie");
275
276 assert!(QueryableCollection::query(&users_kp).filter(|u| u.active).exists(&db));
278
279 let count = QueryableCollection::query(&users_kp).filter(|u| u.active).count(&db);
281 assert_eq!(count, 3);
282 }
283}