1#![doc=include_str!("../README.md")]
2
3use proc_macro::{self, TokenStream};
4use proc_macro2::{Ident as Ident2, Span as Span2, TokenStream as TokenStream2};
5use proc_macro_error::{abort, proc_macro_error};
6use quote::{quote, ToTokens};
7use syn::{
8 parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, token, ItemFn, Token,
9 Type, Visibility,
10};
11
12#[proc_macro_attribute]
14#[proc_macro_error]
15pub fn helper_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
16 let item = parse_macro_input!(item as ItemFn);
17 if item.vis != Visibility::Inherited {
18 abort!(item.vis, "unexpected visibility specifier");
19 }
20 if let Some(x) = item.sig.constness {
21 abort!(x, "helper function cannot be marked const")
22 }
23 if let Some(x) = item.sig.asyncness {
24 abort!(x, "helper function cannot be marked async")
25 }
26 if let Some(x) = item.sig.unsafety {
27 abort!(x, "helper function cannot be marked unsafe")
28 }
29 if let Some(x) = item.sig.abi {
30 abort!(x, "helper function cannot be marked extern")
31 }
32 if let Some(x) = item.sig.variadic {
33 abort!(x, "helper function cannot be variadic")
34 }
35 if item.sig.generics.params.len() != 0 || item.sig.generics.where_clause.is_some() {
36 abort!(item.sig.generics, "helper function cannot have generics")
37 }
38 let inputs = item.sig.inputs.iter();
39 let block = &item.block;
40 let output = &item.sig.output;
41 let name = &item.sig.ident;
42 let fn_name = create_fn_name(name);
43 let captures = parse_macro_input!(attr as CaptureArgsWithTypes);
44 let capture_params = captures.0.iter().map(|x| {
45 let ident = &x.capture.ident;
46 let kind = &x.capture.kind;
47 let ty = &x.ty;
48 quote! { #ident: #kind #ty }
49 });
50 let macro_def = create_macro_def(name, &fn_name, captures.0.iter().map(|x| &x.capture));
51 (quote! {
52 fn #fn_name(#(#capture_params),*, #(#inputs),*) #output {
53 #macro_def
54 #block
55 }
56 #macro_def
57 })
58 .into()
59}
60
61#[proc_macro]
63pub fn use_helper_fn(args: TokenStream) -> TokenStream {
64 let args = parse_macro_input!(args as UseHelperFnArgs);
65
66 return args
67 .0
68 .into_iter()
69 .map(|arg| -> TokenStream {
70 create_macro_def(
71 arg
72 .alias
73 .as_ref()
74 .map(|x| &x.alias)
75 .unwrap_or_else(|| &arg.name),
76 &create_fn_name(&arg.name),
77 arg.captures.0.iter(),
78 )
79 .into()
80 })
81 .flatten()
82 .collect();
83
84 struct UseHelperFnArgs(Punctuated<UseHelperFnArg, Token![,]>);
85
86 impl Parse for UseHelperFnArgs {
87 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
88 Ok(UseHelperFnArgs(input.parse_terminated(<_>::parse)?))
89 }
90 }
91
92 struct UseHelperFnArg {
93 name: syn::Ident,
94 _paren: token::Paren,
95 captures: CaptureArgs,
96 alias: Option<UseHelperFnAlias>,
97 }
98
99 struct UseHelperFnAlias {
100 _as_token: Token![as],
101 alias: syn::Ident,
102 }
103
104 impl Parse for UseHelperFnArg {
105 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
106 let captures;
107 Ok(UseHelperFnArg {
108 name: input.parse()?,
109 _paren: parenthesized!(captures in input),
110 captures: captures.parse()?,
111 alias: input.parse().ok(),
112 })
113 }
114 }
115 impl Parse for UseHelperFnAlias {
116 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
117 Ok(UseHelperFnAlias {
118 _as_token: input.parse()?,
119 alias: input.parse()?,
120 })
121 }
122 }
123}
124
125#[derive(Debug)]
126struct CaptureArgsWithTypes(Punctuated<CaptureArgWithType, Token![,]>);
127
128impl Parse for CaptureArgsWithTypes {
129 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
130 Ok(CaptureArgsWithTypes(input.parse_terminated(<_>::parse)?))
131 }
132}
133
134#[derive(Debug)]
135struct CaptureArgs(Punctuated<CaptureArg, Token![,]>);
136
137impl Parse for CaptureArgs {
138 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
139 Ok(CaptureArgs(input.parse_terminated(<_>::parse)?))
140 }
141}
142
143#[derive(Debug)]
144struct CaptureArg {
145 kind: CaptureArgKind,
146 ident: syn::Ident,
147}
148
149impl Parse for CaptureArg {
150 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
151 Ok(CaptureArg {
152 kind: input.parse()?,
153 ident: input.parse()?,
154 })
155 }
156}
157
158#[derive(Debug)]
159struct CaptureArgWithType {
160 capture: CaptureArg,
161 colon: Token![:],
162 ty: Type,
163}
164
165impl Parse for CaptureArgWithType {
166 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
167 Ok(CaptureArgWithType {
168 capture: input.parse()?,
169 colon: input.parse()?,
170 ty: input.parse()?,
171 })
172 }
173}
174
175#[derive(Debug, Clone)]
176enum CaptureArgKind {
177 Value,
178 Ref(Token![&]),
179 RefMut(Token![&], Token![mut]),
180}
181
182impl Parse for CaptureArgKind {
183 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
184 let lookahead = input.lookahead1();
185 if lookahead.peek(Token![&]) {
186 let ref_token: Token![&] = input.parse()?;
187 let lookahead = input.lookahead1();
188 if lookahead.peek(Token![mut]) {
189 Ok(CaptureArgKind::RefMut(ref_token, input.parse()?))
190 } else {
191 Ok(CaptureArgKind::Ref(ref_token))
192 }
193 } else {
194 Ok(CaptureArgKind::Value)
195 }
196 }
197}
198
199impl ToTokens for CaptureArgKind {
200 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
201 match self {
202 CaptureArgKind::Value => {}
203 CaptureArgKind::Ref(..) => tokens.extend(quote!(&)),
204 CaptureArgKind::RefMut(..) => tokens.extend(quote!(&mut)),
205 }
206 }
207}
208
209fn create_fn_name(name: &Ident2) -> Ident2 {
210 Ident2::new(&format!("__helper_fn_{}", name)[..], Span2::call_site())
211}
212
213fn create_macro_def<'a>(
214 name: &Ident2,
215 fn_name: &Ident2,
216 args: impl Iterator<Item = &'a CaptureArg>,
217) -> TokenStream2 {
218 let capture_args = args.map(|arg| {
219 let ident = &arg.ident;
220 match &arg.kind {
221 CaptureArgKind::Value => quote! { #ident },
222 CaptureArgKind::Ref(..) => quote! {{
223 trait AutoRef { fn auto_ref(&self) -> &Self { self } }
224 impl<T> AutoRef for T {}
225 #ident.auto_ref()
226 }},
227 CaptureArgKind::RefMut(..) => quote! {{
228 trait AutoRefMut { fn auto_ref_mut(&mut self) -> &mut Self { self } }
229 impl<T> AutoRefMut for T {}
230 #ident.auto_ref_mut()
231 }},
232 }
233 });
234 quote! {
235 macro_rules! #name {
236 ($($arg:expr),* $(,)?) => {
237 #fn_name(#(#capture_args),*, $($arg),*)
238 }
239 }
240 }
241}