1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4
5use proc_macro2::TokenStream as TokenStream2;
6use syn::{Error, ImplItem, TraitItem};
7
8use quote::quote;
9use util::{SplitArgs, split_idents};
10use visit::IntoAsync;
11
12use crate::{parse::Item, visit::IntoSync};
13
14mod parse;
15mod util;
16mod visit;
17
18fn into_sync(input: &mut Item) -> TokenStream2 {
19 match input {
20 Item::Impl(item) => {
21 for inner in &mut item.items {
22 if let ImplItem::Fn(method) = inner {
23 method.sig.asyncness = None;
24 }
25 }
26 IntoSync.into_sync(quote!(#item))
27 }
28 Item::Trait(item) => {
29 for inner in &mut item.items {
30 if let TraitItem::Fn(method) = inner {
31 method.sig.asyncness = None;
32 }
33 }
34 IntoSync.into_sync(quote!(#item))
35 }
36 Item::Fn(item) => {
37 item.sig.asyncness = None;
38 IntoSync.into_sync(quote!(#item))
39 } }
41}
42
43fn into_async(input: &Item) -> TokenStream2 {
44 match input {
45 Item::Impl(item) => IntoAsync.into_async(quote!(#item)),
46 Item::Trait(item) => IntoAsync.into_async(quote!(#item)),
47 Item::Fn(item) => IntoAsync.into_async(quote!(#item)),
48 }
50}
51
52fn split_(args: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
53 let idents: Option<SplitArgs> = (!args.is_empty()).then(|| syn::parse(args)).transpose()?;
54
55 let item: Item = syn::parse(input)?;
56
57 let (mut sync_item, async_item) = match item {
58 Item::Fn(item_fn) => {
59 if item_fn.sig.asyncness.is_none() {
60 return Err(Error::new_spanned(&item_fn.sig, "function must be async"));
61 }
62 let idents = idents.unwrap_or_else(|| split_idents(&item_fn.sig.ident).into());
63 let mut sync_item = item_fn.clone();
64 sync_item.sig.ident = idents.0.sync_ident;
65 let mut async_item = item_fn;
66 async_item.sig.ident = idents.0.async_ident;
67 (Item::Fn(sync_item), Item::Fn(async_item))
68 }
69 _ => unimplemented!(),
70 };
71
72 let mut sync_ts = into_sync(&mut sync_item);
73 let async_ts = into_async(&async_item);
74 sync_ts.extend(async_ts);
75 Ok(sync_ts.into())
76}
77
78#[proc_macro_attribute]
79pub fn split(args: TokenStream, input: TokenStream) -> TokenStream {
80 split_(args, input).unwrap_or_else(|err| err.to_compile_error().into())
81}