chain_linq/lib.rs
1/*!
2# Chain-LINQ
3
4This crate is an implementation of .NET's LINQ query syntax in rust as a declarative macro. The declarative macro is generated by another declarative macro, specifically the
5branching_parser from my other crate, big_mac.
6
7Available statements closely mirror standard LINQ operations; notably join is missing, as I was unable to find equivalent functionality from iterators.
8
9Generally, each statement maps to an iterator method; here is a list of them, with a description and equivalent method if present:
10- from # in #: selects and names variables from a collection. Maps to into_iter().
11- select #: ends a linq query and returns an iterator. Maps to map().
12- select # into #: partially ends a linq query and puts an iterator into a range variable. Creates a continuation.
13- where #: selects only elements that match a particular criteria. Maps to filter().
14- let # = #: creates a range element. Does not map to a method.
15- collect #: calls .collect().
16- collect # as #: calls .collect with a generic type. Follows select syntax but the as parameter is the destination type.
17
18**REQUIRES [ITERTOOLS](https://docs.rs/itertools/0.10.1/itertools/):**
19- orderby #: sorts iterator ascending by a criteria. Maps to unstable_sort_by_key().
20- orderby # ascending: sorts iterator ascending by a criteria. Maps to unstable_sort_by_key().
21- orderby # descending: sorts iterator descending by a criteria. Maps to unstable_sort_by_key().rev().
22- group # by #: groups elements into groups based on some criteria and returns the result. Maps to group_by().
23- group # by # into #: groups elements into groups based on some criteria, and then creates a continuation.. Maps to group_by().
24
25For more explanation of how LINQ works, check Microsoft's docs [here](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/)
26
27Also useful: [Keyword breakdowns](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/query-keywords)
28
29## Examples
30
31```
32use chain_linq::linq;
33
34let xs = [(3, 1), (2, 2), (1, 3)];
35
36let x = linq!(
37 from (x, y) in xs
38 let z = x + y
39 select z into zs
40 from z in zs
41 select z * 2
42);
43```
44
45```
46use chain_linq::linq;
47
48let xss = [vec!(27, 13, 12), vec!(69), vec!(76, 7, 420)];
49
50let x = linq!(
51 from xs in xss
52 group xs by xs.len() into gs
53 from iter in gs
54 from x in iter
55 collect x + 1 as Vec<i32>
56);
57```
58
59```
60use chain_linq::linq;
61
62let xss = [vec!(27, 13, 12), vec!(69), vec!(76, 7, 420)];
63
64let x = linq!(
65 from xs in xss
66 from x in xs.into_iter().rev()
67 collect x as Vec<i32>
68);
69```
70
71*/
72
73// Patterns:
74// from # in #
75// where #
76// select #
77// select # into #
78// orderby #
79// orderby # ascending/descending
80// group # by #
81// group # by # into #
82// let # = #
83
84mod macros {
85 #[macro_export]
86 macro_rules! linq_impl {
87 // Archetype: {name of working variable} {cumulative previous expression} {statements to add to closures as prefixes} {pattern to match, remaining tokens}
88 // Short names: {var} {prev} {prefix} {___, toks}
89
90 // From clauses
91 (from {$var:pat} in {$base:expr}, $($toks:tt)*) => ({
92 $crate::linq_impl!{
93 {$var}
94 {$base.into_iter()}
95 {}
96 {$($toks)*}
97 }
98 });
99
100 (
101 {$var:pat}
102 {$prev:expr}
103 {$($prefix:stmt)*}
104 {from {$new_var:pat} in {$new_base:expr}, $($toks:tt)*}
105 ) => {
106 $crate::linq_impl!{
107 {$new_var}
108 {$prev.flat_map(|$var| { $($prefix)* $new_base.into_iter() })}
109 {}
110 {$($toks)*}
111 }
112 };
113
114 // Where clause
115 (
116 {$var:pat}
117 {$prev:expr}
118 {$($prefix:stmt)*}
119 {where {$cond:expr}, $($toks:tt)*}
120 ) => {
121 $crate::linq_impl!{
122 {$var}
123 {$prev.filter(|$var| { $($prefix)* $cond })}
124 {}
125 {$($toks)*}
126 }
127 };
128
129 // Select clauses
130 (
131 {$var:pat}
132 {$prev:expr}
133 {$($prefix:stmt)*}
134 {select {$result:expr} into {$new_base:ident}, $($toks:tt)*}
135 ) => {
136 let $new_base = $prev.map(|$var| { $($prefix)* $result });
137
138 $crate::linq_impl!{ $($toks)* }
139 };
140
141 (
142 {$var:pat}
143 {$prev:expr}
144 {$($prefix:stmt)*}
145 {select {$result:expr},}
146 ) => {
147 use ::std::iter::*;
148
149 $prev.map(|$var| { $($prefix)* $result })
150 };
151
152 // Orderby clauses
153 (
154 {$var:pat}
155 {$prev:expr}
156 {$($prefix:stmt)*}
157 {orderby {$key:expr}, $($toks:tt)*}
158 ) => {
159 $crate::linq_impl!{
160 {$var}
161 {$prev.sorted_unstable_by_key(|$var| { $($prefix)* $key })}
162 {}
163 {$($toks)*}
164 }
165 };
166
167 (
168 {$var:pat}
169 {$prev:expr}
170 {$($prefix:stmt)*}
171 {orderby {$key:expr} ascending, $($toks:tt)*}
172 ) => {
173 $crate::linq_impl!{
174 {$var}
175 {$prev.sorted_unstable_by_key(|$var| { $($prefix)* $key })}
176 {}
177 {$($toks)*}
178 }
179 };
180
181 (
182 {$var:pat}
183 {$prev:expr}
184 {$($prefix:stmt)*}
185 {orderby {$key:expr} descending, $($toks:tt)*}
186 ) => {
187 $crate::linq_impl!{
188 {$var}
189 {$prev.sorted_unstable_by_key(|$var| { $($prefix)* $key }).rev()}
190 {}
191 {$($toks)*}
192 }
193 };
194
195 // Group clauses
196 (
197 {$var:pat}
198 {$prev:expr}
199 {$($prefix:stmt)*}
200 {group {$grouped:ident} by {$grouper:expr} into {$group:ident}, $($toks:tt)*}
201 ) => {
202 {
203 use ::itertools::*;
204
205 let $group = $prev.group_by(|$grouped| { $($prefix)* $grouper }).into_iter().map(|g| g.1)
206
207 $crate::linq_impl!{ $($toks)* }
208 }
209
210 };
211
212 (
213 {$var:pat}
214 {$prev:expr}
215 {$($prefix:stmt)*}
216 {group {$grouped:ident} by {$grouper:expr}, $($toks:tt)*}
217 ) => {
218 {
219 use ::std::iter::*;
220 use ::itertools::*;
221
222 $prev.group_by(|$grouped| { $($prefix)* $grouper })
223 }
224 };
225
226 // Let clause
227 (
228 {$var:pat}
229 {$prev:expr}
230 {$($prefix:stmt)*}
231 {let {$new_var:pat} = {$query:expr}, $($toks:tt)*}
232 ) => {
233 $crate::linq_impl!{
234 {$var}
235 {$prev}
236 {$($prefix)* let $new_var = $query }
237 {$($toks)*}
238 }
239 };
240
241 // Collect clause
242 (
243 {$var:pat}
244 {$prev:expr}
245 {$($prefix:stmt)*}
246 {collect {$result:expr},}
247 ) => {
248 use ::std::iter::*;
249
250 $prev.map(|$var| { $($prefix)* $result }).collect()
251 };
252
253 (
254 {$var:pat}
255 {$prev:expr}
256 {$($prefix:stmt)*}
257 {collect {$result:expr} as {$collection:ty},}
258 ) => {
259 use ::std::iter::*;
260
261 $prev.map(|$var| { $($prefix)* $result }).collect::<$collection>()
262 };
263
264 (
265 {$var:pat}
266 {$prev:expr}
267 {$($prefix:stmt)*}
268 {}
269 ) => {
270 compile_error!("Unfinished linq query!");
271 };
272 }
273
274 big_mac::branching_parser!(
275 @unroll
276 chain_linq;
277 linq
278 linq_parser
279 linq_filter
280 chain_linq::linq_impl;
281 {
282 from
283 {
284 #
285 {
286 in
287 {
288 # {}
289 }
290 }
291 }
292 }
293 {
294 where
295 {
296 # {}
297 }
298 }
299 {
300 select
301 {
302 #
303 {
304 into
305 {
306 # {}
307 }
308 }
309 {}
310 }
311 }
312 {
313 orderby
314 {
315 #
316 {}
317 {
318 ascending
319 }
320 {
321 descending
322 }
323 }
324 }
325 {
326 group
327 {
328 #
329 {
330 by
331 {
332 #
333 {}
334 {
335 into
336 {
337 # {}
338 }
339 }
340 }
341 }
342 }
343 }
344 {
345 let
346 {
347 #
348 {
349 =
350 {
351 # {}
352 }
353 }
354 }
355 }
356 {
357 collect
358 {
359 #
360 {}
361 {
362 as
363 {
364 # {}
365 }
366 }
367 }
368 }
369 );
370
371 #[allow(unused_imports)]
372 pub(crate) use {linq, linq_parser, linq_filter, linq_impl};
373}