Skip to main content

helper_fn/
lib.rs

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/// See the [crate level documentation](index.html).
13#[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/// See the [crate level documentation](index.html).
62#[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}