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