loop_code/
lib.rs

1//! # Loop code
2//!
3//! `loop_code` gives you macro `loop_code::repeat` which allows you to repeat blocks of code an arbitrary number of times.
4//!
5
6use proc_macro2::{Span, TokenStream};
7use quote::{quote, ToTokens};
8use syn::{
9    parse::{Parse, ParseStream},
10    parse_macro_input, Block, LitInt, Result,
11};
12use syn::{parse2, Ident};
13
14extern crate proc_macro;
15
16struct SimpleCodeLoop {
17    loops: usize,
18    block: Block,
19}
20impl Parse for SimpleCodeLoop {
21    fn parse(input: ParseStream) -> Result<Self> {
22        let loops: LitInt = input.parse()?;
23        let block: Block = input.parse()?;
24
25        let loops: usize = loops.base10_parse()?;
26
27        Ok(Self { loops, block })
28    }
29}
30
31struct AdvancedCodeLoop {
32    index_variable: Ident,
33    loops: usize,
34    number_type: Ident,
35    block: Block,
36}
37impl Parse for AdvancedCodeLoop {
38    fn parse(input: ParseStream) -> Result<Self> {
39        let index_variable: Ident = input.parse()?;
40        let number_type: Ident = input
41            .parse()
42            .unwrap_or_else(|_| Ident::new("usize", Span::call_site()));
43        let loops: LitInt = input.parse()?;
44        let block: Block = input.parse()?;
45
46        let loops: usize = loops.base10_parse()?;
47
48        Ok(Self {
49            index_variable,
50            loops,
51            number_type,
52            block,
53        })
54    }
55}
56
57/// Repeats code n times
58/// ### Simple example
59/// ```
60/// use loop_code::repeat;
61///
62/// repeat!(5 {
63///     println!("Hello world");
64/// });
65/// ```
66/// ---
67/// ### Loop with indexing
68/// ```
69/// use loop_code::repeat;
70///
71/// repeat!(INDEX 5 {
72///     println!("Index: {}", INDEX);
73/// });
74/// ```
75/// ---
76/// ### Change index type
77/// ```
78/// use loop_code::repeat;
79///
80/// repeat!(I i32 5 {
81///     println!("Index: {}", I - 32);
82/// });
83/// ```
84#[proc_macro]
85pub fn repeat(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
86    let input = TokenStream::from(input);
87    if let Ok(code_loop) = parse2::<AdvancedCodeLoop>(input.clone()) {
88        let mut code = TokenStream::new();
89        for i in 0..code_loop.loops {
90            let block = &code_loop.block;
91            let variable = &code_loop.index_variable;
92            let kind = &code_loop.number_type;
93
94            let lit = LitInt::new(&i.to_string(), Span::call_site());
95
96            quote! {
97                {
98                    const #variable: #kind = #lit;
99                    #block
100                }
101            }
102            .to_tokens(&mut code);
103        }
104
105        code.into()
106    } else {
107        let input: proc_macro::TokenStream = input.into();
108        let code_loop = parse_macro_input!(input as SimpleCodeLoop);
109
110        let mut blocks = TokenStream::new();
111        for _ in 0..code_loop.loops {
112            code_loop.block.to_tokens(&mut blocks);
113        }
114
115        blocks.into()
116    }
117}