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