lender_derive/lib.rs
1/*
2 * SPDX-FileCopyrightText: 2023 Tommaso Fontana
3 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
4 *
5 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later OR MIT
6 */
7
8#![doc = include_str!("../README.md")]
9
10use proc_macro::TokenStream;
11use quote::quote;
12use syn::{
13 parse::{Parse, ParseStream, Result},
14 parse_macro_input,
15 token::In,
16 Block, Expr, Pat,
17};
18
19struct ForLenderInfo {
20 pub pat: Pat,
21 pub _in_token: In,
22 pub expr: Expr,
23 pub body: Block,
24}
25
26impl Parse for ForLenderInfo {
27 fn parse(input: ParseStream) -> Result<Self> {
28 Ok(ForLenderInfo {
29 pat: Pat::parse_multi(input)?, // We allow for the | operator
30 _in_token: input.parse()?,
31 expr: Expr::parse_without_eager_brace(input)?, // As in the for loop syntax
32 body: input.parse()?,
33 })
34 }
35}
36
37/**
38
39Syntax sugar for iterating over a [`Lender`](https://docs.rs/lender/latest/lender/trait.Lender.html).
40
41This function-like procedural macro expands a syntax of the form
42```[ignore]
43for_!(PATTERN in EXPR BLOCK);
44```
45where `PATTERN` is a valid pattern for a `for` loop, `EXPR` is an expression that
46implements [`IntoLender`](https://docs.rs/lender/latest/lender/trait.IntoLender.html) and `BLOCK` is a block of code, into a `while let` loop that
47iterates over a [`Lender`](https://docs.rs/lender/latest/lender/trait.Lender.html) obtained from the [`IntoLender`](https://docs.rs/lender/latest/lender/trait.IntoLender.html):
48```[ignore]
49let mut ___ඞඞඞlenderඞඞඞ___ = (EXPR).into_lender();
50while let Some(PATTERN) = ___ඞඞඞlenderඞඞඞ___.next() BLOCK
51```
52For example, the following code
53```[ignore]
54for_!(x in 0..10 {
55 println!("{}", x);
56});
57```
58iterates over the integers [0. .10), printing them,
59using a [`Lender`](https://docs.rs/lender/latest/lender/trait.Lender.html) obtained by
60automagically adapting an [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) (in this case, a [`Range`](https://doc.rust-lang.org/std/ops/struct.Range.html)).
61
62Note that the outer parentheses are part of the standard Rust syntax for function-like
63procedural macros, and thus can be replaced, for example, with brackets.
64
65For an example of a more complex usage, see the following code, which iterates over
66the elements of an `enum`, but only on the first two variants:
67```[ignore]
68#[derive(Debug)]
69enum Three {
70 A,
71 B,
72 C,
73}
74
75#[test]
76pub fn test_bar() {
77 for_!(x @ (Three::A | Three::B) in [Three::A, Three::B, Three::C].into_into_lender() {
78 dbg!(x);
79 });
80}
81```
82In this case, since an array is an [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html),
83but not an [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html), we need to
84adapt it manually.
85
86Note that these examples have the sole purpose of showing the syntax of the macro:
87in these cases a standard iterator would be simpler and more efficient.
88*/
89#[proc_macro]
90pub fn for_(input: TokenStream) -> TokenStream {
91 let ForLenderInfo {
92 pat,
93 _in_token,
94 expr,
95 body,
96 } = parse_macro_input!(input as ForLenderInfo);
97
98 quote! {{
99 use lender::{Lender, IntoLender};
100 let mut ___ඞඞඞlenderඞඞඞ___ = (#expr).into_lender();
101 while let Some( #pat ) = ___ඞඞඞlenderඞඞඞ___.next() #body
102 }}
103 .into()
104}