1use proc_macro2::Group;
2use quote::{quote, quote_spanned};
3
4#[proc_macro]
21pub fn ct_for(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
22 let args = proc_macro2::TokenStream::from(args);
23 let mut iter = args.into_iter();
24
25 let subst_name = match iter.next() {
26 Some(proc_macro2::TokenTree::Ident(ident)) => ident,
27 Some(tok) => return error_span("Expected identifier", tok.span()),
28 None => return error("Unexpected end of macro"),
29 };
30
31 match iter.next() {
32 Some(proc_macro2::TokenTree::Ident(ident)) if ident == "in" => {}
33 Some(tok) => return error_span("Expected 'in'", tok.span()),
34 None => return error("Unexpected end of macro"),
35 }
36
37 let subst_group = match iter.next() {
38 Some(proc_macro2::TokenTree::Group(group)) => group,
39 Some(tok) => return error_span("Expected (...)", tok.span()),
40 None => return error("Unexpected end of macro"),
41 };
42
43 let subst_values = collect_subst_values(subst_group.stream().into_iter());
44
45 match iter.next() {
46 Some(proc_macro2::TokenTree::Ident(ident)) if ident == "do" => {}
47 Some(tok) => return error_span("Expected 'do'", tok.span()),
48 None => return error("Unexpected end of macro"),
49 }
50
51 let mut res = proc_macro2::TokenStream::new();
52
53 for subst in subst_values {
54 let iter = iter.clone();
55 res.extend(subst_recursive(iter, subst_name.clone(), subst));
56 }
57
58 res.into()
59}
60
61fn collect_subst_values(
62 iter: impl Iterator<Item = proc_macro2::TokenTree>,
63) -> Vec<proc_macro2::TokenStream> {
64 let mut res = Vec::new();
65
66 let mut val = proc_macro2::TokenStream::new();
67 for tok in iter {
68 match tok {
69 proc_macro2::TokenTree::Punct(punct) if punct.as_char() == ',' => {
70 res.push(val);
71 val = proc_macro2::TokenStream::new();
72 }
73 _ => {
74 val.extend(std::iter::once(tok));
75 }
76 }
77 }
78 if !val.is_empty() {
79 res.push(val);
80 }
81
82 res
83}
84
85fn subst_recursive(
86 iter: impl Iterator<Item = proc_macro2::TokenTree>,
87 subst_name: proc_macro2::Ident,
88 subst_value: proc_macro2::TokenStream,
89) -> proc_macro2::TokenStream {
90 let mut res = proc_macro2::TokenStream::new();
91
92 for tok in iter {
93 match tok {
94 proc_macro2::TokenTree::Group(group) => {
95 let group_tokens = subst_recursive(
96 group.stream().into_iter(),
97 subst_name.clone(),
98 subst_value.clone(),
99 );
100 res.extend(std::iter::once(proc_macro2::TokenTree::Group(Group::new(
101 group.delimiter(),
102 group_tokens,
103 ))));
104 }
105 proc_macro2::TokenTree::Ident(ident) if ident == subst_name => {
106 res.extend(subst_value.clone());
107 }
108 _ => {
109 res.extend(std::iter::once(tok));
110 }
111 }
112 }
113
114 res
115}
116
117fn error(msg: &str) -> proc_macro::TokenStream {
118 quote! {
119 compile_error!(#msg);
120 }
121 .into()
122}
123
124fn error_span(msg: &str, span: proc_macro2::Span) -> proc_macro::TokenStream {
125 quote_spanned! {span=>
126 compile_error!(#msg);
127 }
128 .into()
129}