iter_python/
macros.rs

1pub use ::join_lazy_fmt::lazy_format;
2
3/// (shortname: `i!`) — Write the most pervasive iterator adapters
4/// ([filter]ing and [map]ping) as [Python generator expressions].
5///
6/// # Examples
7///
8/// ### Squaring even numbers
9///
10/// ```rust
11/// use ::iter_python::prelude::*;
12///
13/// let mut all_evens_squared = i!(
14///     x * x
15///     for x in (0 ..)
16///     if x % 2 == 0
17/// );
18/// assert_eq!(all_evens_squared.next(), Some(0));
19/// assert_eq!(all_evens_squared.next(), Some(4));
20/// assert_eq!(all_evens_squared.next(), Some(16));
21/// ```
22///
23/// ### `filter`ing is optional, such as in Python:
24///
25/// ```rust
26/// use ::iter_python::prelude::*;
27///
28/// let mut numbers_binary = i!(format!("{:02b}", x) for x in 1 ..= 3);
29///
30/// assert_eq!(numbers_binary.next(), Some("01".into()));
31/// assert_eq!(numbers_binary.next(), Some("10".into()));
32/// assert_eq!(numbers_binary.next(), Some("11".into()));
33/// assert_eq!(numbers_binary.next(), None);
34/// ```
35///
36/// ### You may also `filter` with `if let`:
37///
38/// ```rust
39/// use ::iter_python::prelude::*;
40///
41/// let strings = ["42", "0", "zero", "27"];
42///
43/// let parsed_as_i32s = i!(s.parse::<i32>() for &s in &strings);
44///
45/// let total: i32 = Iterator::sum(i!(
46///     x
47///     for res in parsed_as_i32s
48///     if let Ok(x) = res
49/// ));
50///
51/// assert_eq!(total, 42 + 0 + 27);
52/// ```
53///
54/// ```rust
55/// use ::iter_python::prelude::*;
56///
57/// enum Fruit { Banana, Peach, RottenApple }
58/// use Fruit::*;
59///
60/// impl Fruit {
61///     fn is_fresh (&self) -> bool
62///     {
63///         if let RottenApple = self {
64///             false
65///         } else {
66///             true
67///         }
68///     }
69/// }
70///
71/// static BASKET: &[Fruit] = &[Banana, RottenApple, Peach, Banana];
72///
73/// let no_rotten_apple = i!(
74///     fruit
75///     for fruit in BASKET
76///     if let Banana | Peach = fruit
77/// );
78///
79/// assert!({no_rotten_apple}.all(Fruit::is_fresh));
80/// ```
81///
82/// ### You can also nest / combine iterators
83///
84/// ```rust
85/// use ::iter_python::prelude::*;
86///
87/// assert_eq!(
88///     i!((i, j) for i in 0 .. 3 for j in 0 .. 2).vec(),
89///     vec![
90///         (0, 0),
91///         (0, 1),
92///         (1, 0),
93///         (1, 1),
94///         (2, 0),
95///         (2, 1),
96///     ],
97/// );
98/// ```
99///
100/// #### With the same `if` guards as with the single case:
101///
102/// ```rust
103/// use ::iter_python::prelude::*;
104///
105/// assert_eq!(
106///     i!(
107///         (i, j)
108///         for i in 0 .. 4
109///         if i != 2
110///         for j in 0 .. i
111///         if j != 1
112///     ).vec(),
113///     vec![
114///         (1, 0),
115///         (3, 0),
116///         (3, 2),
117///     ]
118/// )
119/// ```
120/// [filter]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.filter
121/// [map]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.map
122/// [Python generator expressions]: https://www.python.org/dev/peps/pep-0289
123/// [`if let`]: https://doc.rust-lang.org/book/if-let.html
124#[macro_export]
125macro_rules! iter {
126    (
127        $($input:tt)*
128    ) => ($crate::__munch_iter_args! {
129        munching[
130            $($input)*
131        ]
132        current_elem[]
133        to_endpoint[]
134    });
135}
136#[doc(inline)]
137pub use iter;
138
139/// Split the input using `for` and `if`.
140#[doc(hidden)] /** Not part of the public API */ #[macro_export]
141macro_rules! __munch_iter_args {
142    // New `for` *nested* (no `if` in between)
143    (
144        munching[
145            for $($rest:tt)*
146        ]
147        current_elem[
148            for $($acc_for:tt)*
149        ]
150        to_endpoint[
151            $($out:tt)*
152        ]
153    ) => ($crate::__munch_iter_args! {
154        munching[
155            $($rest)*
156        ]
157        current_elem[ for ]
158        to_endpoint[
159            $($out)*
160            [for $($acc_for)*]
161            [] // encountered `for` after `for` => no `if` guard
162        ]
163    });
164
165    // New `for`
166    (
167        munching[
168            for $($rest:tt)*
169        ]
170        current_elem $current_elem:tt
171        to_endpoint[
172            $($out:tt)*
173        ]
174    ) => ($crate::__munch_iter_args! {
175        munching[
176            $($rest)*
177        ]
178        current_elem[ for ]
179        to_endpoint[
180            $($out)*
181            $current_elem
182        ]
183    });
184
185    // New `if`
186    (
187        munching[
188            if $($rest:tt)*
189        ]
190        current_elem $current_elem:tt
191        to_endpoint[
192            $($out:tt)*
193        ]
194    ) => ($crate::__munch_iter_args! {
195        munching[
196            $($rest)*
197        ]
198        current_elem[ if ]
199        to_endpoint[
200            $($out)*
201            $current_elem
202        ]
203    });
204
205    // Neither `for` nor `if`: just accumulate that `:tt`
206    (
207        munching[
208            $current_tt:tt $($rest:tt)*
209        ]
210        current_elem[
211            $($current_elem:tt)*
212        ]
213        to_endpoint $to_endpoint:tt
214    ) => ($crate::__munch_iter_args! {
215        munching[
216            $($rest)*
217        ]
218        current_elem[
219            $($current_elem)* $current_tt
220        ]
221        to_endpoint $to_endpoint
222    });
223
224    // END: nothing left to munch
225    (
226        munching[]
227        current_elem $current_elem:tt
228        to_endpoint[
229            $($out:tt)*
230        ]
231    ) => ($crate::__endpoint! {
232        $($out)*
233        $current_elem
234    })
235}
236
237#[doc(hidden)] /** Not part of the public API */ #[macro_export]
238macro_rules! __endpoint {
239    // Non-last `for`-&-`if`
240    (
241        $mapped_expr:tt
242        [for $var:pat in $iterable:expr]
243        [if $($filter:tt)*]
244        $($rest:tt)+
245    ) => (
246        $crate::__::core::iter::Iterator::flat_map(
247            $crate::__::core::iter::IntoIterator::into_iter(
248                $iterable
249            ),
250            move |$var| {
251                $crate::__::core::iter::Iterator::flatten(
252                    $crate::__::core::iter::IntoIterator::into_iter(
253                        if $($filter)* {
254                            $crate::__::core::option::Option::Some(
255                                $crate::__endpoint!(
256                                    $mapped_expr
257                                    $($rest)*
258                                )
259                            )
260                        } else {
261                            $crate::__::core::option::Option::None
262                        }
263                    )
264                )
265            },
266        )
267    );
268
269    // Non-last `for` (no `if`!)
270    (
271        $mapped_expr:tt
272        [for $var:pat in $iterable:expr]
273        [/* no filter */]
274        $($rest:tt)+
275    ) => (
276        $crate::__::core::iter::Iterator::flat_map(
277            $crate::__::core::iter::IntoIterator::into_iter(
278                $iterable
279            ),
280            move |$var| $crate::__endpoint!($mapped_expr $($rest)*),
281        )
282    );
283
284    // Last `for`-&-`if`
285    (
286        [$mapped_expr:expr]
287        [for $var:pat in $iterable:expr]
288        [if $($filter:tt)*]
289        /* no rest */
290    ) => (
291        $crate::__::core::iter::Iterator::filter_map(
292            $crate::__::core::iter::IntoIterator::into_iter(
293                $iterable
294            ),
295            move |$var| if $($filter)* {
296                $crate::__::core::option::Option::Some($mapped_expr)
297            } else {
298                $crate::__::core::option::Option::None
299            },
300        )
301    );
302
303    // Last `for` (no `if`!)
304    (
305        [$mapped_expr:expr]
306        [for $var:pat in $iterable:expr]
307        $([/* no filter */])?
308        /* no rest */
309    ) => (
310        $crate::__::core::iter::Iterator::map(
311            $crate::__::core::iter::IntoIterator::into_iter(
312                $iterable
313            ),
314            move |$var| $mapped_expr,
315        )
316    );
317}
318
319/// (shortname: `v!`) — [Python "list" comprehensions]: same as [`i!`],
320/// but [`collect`]ed into a [`Vec`] instead.
321///
322/// # `v!` or `vec!`?
323///
324/// **[`v!`] fallbacks to [`::std::vec!`] functionality**,
325/// thus allowing maximum compatiblity!
326///
327/// ### Example
328/// ```rust
329/// use ::iter_python::macros::vec;
330///
331/// let v1 = vec![i for i in 1 ..= 4];
332/// let v2 = vec![1, 2, 3, 4];
333/// assert_eq!(v1, v2);
334/// ```
335///
336/// It has not been named `vec` to prevent lints against ambiguous blob imports.
337///
338/// [`i!`]: `iter`
339/// [`collect`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.collect
340/// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
341/// [Python "list" comprehensions]: https://www.python.org/dev/peps/pep-0202
342/// [`v!`]: `vec`
343/// [`::std::vec!`]: https://doc.rust-lang.org/std/macro.vec.html
344#[cfg(feature = "std")]
345#[cfg_attr(feature = "better-docs",
346    doc(cfg(feature = "std")),
347)]
348#[macro_export]
349macro_rules! v {
350    // std syntax compat.
351    (
352        $($e:expr),* $(,)?
353    ) => (
354        $crate::__::std::vec! { $($e),* }
355    );
356
357    // std syntax compat.
358    (
359        $e:expr ; $count:expr
360    ) => (
361        $crate::__::std::vec! { $e; $count }
362    );
363
364    (
365        $($otherwise:tt)*
366    ) => (
367        <
368            $crate::__::std::vec::Vec<_>
369            as
370            $crate::__::core::iter::FromIterator<_>
371        >::from_iter(
372            $crate::macros::iter!( $($otherwise)* )
373        )
374    );
375}
376
377#[cfg(feature = "std")]
378#[cfg_attr(feature = "better-docs",
379    doc(cfg(feature = "std")),
380)]
381#[doc(inline)]
382pub use crate::v as vec;