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}