1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*
 * SPDX-FileCopyrightText: 2023 Tommaso Fontana
 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
 *
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later OR MIT
 */

#![doc = include_str!("../README.md")]

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{
    parse::{Parse, ParseStream, Result},
    parse_macro_input,
    token::In,
    Block, Expr, Pat,
};

struct ForLenderInfo {
    pub pat: Box<Pat>,
    pub _in_token: In,
    pub expr: Box<Expr>,
    pub body: Block,
}

impl Parse for ForLenderInfo {
    fn parse(input: ParseStream) -> Result<Self> {
        Ok(ForLenderInfo {
            pat: Box::new(Pat::parse_multi(input)?),
            _in_token: input.parse()?,
            expr: input.parse()?,
            body: input.parse()?,
        })
    }
}

/**

Syntax sugar for iterating over a `Lender`.

This function-like macro expands a syntax of the form
```[ignore]
for_!(PATTERN in EXPR BLOCK);
```
where `PATTERN` is a valid pattern for a `for` loop, `EXPR` is an expression that
implements [`IntoLender`](https://docs.rs/lender/latest/lender/trait.IntoLender.html) and `BLOCK` is a block of code, into a `while let` loop that
iterates over a `Lender` obtained from the [`IntoLender`](https://docs.rs/lender/latest/lender/trait.IntoLender.html):
```[ignore]
let mut ___ඞඞඞlenderඞඞඞ___ = (EXPR).into_lender();
while let Some(PATTERN) = ___ඞඞඞlenderඞඞඞ___.next() BLOCK
```
For example, the following code
```[ignore]
for_!(x in from_into_iter(0..10) {
    println!("{}", x);
});
```
iterates over the integers [0. .10), printing them,
using a [`Lender`](https://docs.rs/lender/latest/lender/trait.Lender.html) obtained by
adapting an `IntoIterator` (in this case, a `Range`).

For an example of a more complex usage, see the following code, which iterates over
the elements of an `enum`, but only on the first two variants:
```[ignore]
#[derive(Debug)]
enum Three {
    A,
    B,
    C,
}

#[test]
pub fn test_bar() {
    for_!(x @ (Three::A | Three::B) in from_into_iter([Three::A, Three::B, Three::C]) {
        dbg!(x);
    });
}
```

*/
#[proc_macro]
pub fn for_(input: TokenStream) -> TokenStream {
    let ForLenderInfo {
        pat,
        _in_token,
        expr,
        body,
    } = parse_macro_input!(input as ForLenderInfo);

    quote! {{
        let mut ___ඞඞඞlenderඞඞඞ___ = (#expr).into_lender();
        while let Some( #pat ) = ___ඞඞඞlenderඞඞඞ___.next() #body
    }}
    .into()
}