implicit_await_macro/
lib.rs1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::{TokenStream as TokenStream2, TokenTree, Span};
5use syn::{parse_macro_input, parse_quote, ItemFn, Type, AngleBracketedGenericArguments, PathArguments, Ident};
6use syn::fold::Fold;
7
8#[proc_macro_attribute]
9pub fn implicit_await(_attr: TokenStream, input: TokenStream) -> TokenStream {
10 let item_fn = parse_macro_input!(input as ItemFn);
11 let mut folder = ImplicitAwaitFold { defer: 0 };
12 let transformed_fn = folder.fold_item_fn(item_fn);
13 quote::quote!(#transformed_fn).into()
14}
15
16struct ImplicitAwaitFold {
17 defer: usize
18}
19
20impl Fold for ImplicitAwaitFold {
21 fn fold_expr(&mut self, expr: syn::Expr) -> syn::Expr {
22 let subfolded = syn::fold::fold_expr(self, expr);
23 match (self.defer, &subfolded) {
24 (0, syn::Expr::Call(call)) => {
25 parse_quote!({{ #call }.as_future().await})
26 },
27 (0, syn::Expr::MethodCall(method_call)) => {
28 parse_quote!({{ #method_call }.as_future().await})
29 },
30 _ => subfolded
31 }
32 }
33
34 fn fold_impl_item_method(&mut self, method: syn::ImplItemMethod) -> syn::ImplItemMethod {
35 let mut subfolded = syn::fold::fold_impl_item_method(self, method);
36 subfolded.block.stmts.insert(0, parse_quote!(use implicit_await::as_future::FutureAsFuture;));
37 subfolded.block.stmts.insert(0, parse_quote!(use implicit_await::as_future::NonFutureAsFuture;));
38 subfolded
39 }
40
41 fn fold_item_fn(&mut self, func: syn::ItemFn) -> syn::ItemFn {
42 let mut subfolded = syn::fold::fold_item_fn(self, func);
43 subfolded.block.stmts.insert(0, parse_quote!(use implicit_await::as_future::FutureAsFuture;));
44 subfolded.block.stmts.insert(0, parse_quote!(use implicit_await::as_future::NonFutureAsFuture;));
45 subfolded
46 }
47
48 fn fold_expr_macro(&mut self, mac: syn::ExprMacro) -> syn::ExprMacro {
49 let is_defer = mac.mac.path.segments.iter()
50 .last()
51 .map_or(false, |segment| segment.ident.to_string() == "defer");
52 if is_defer {
53 self.defer += 1;
54 }
55 let subfolded = syn::fold::fold_expr_macro(self, mac);
56 if is_defer {
57 self.defer -= 1;
58 }
59 subfolded
60 }
61}
62
63#[proc_macro]
65pub fn as_future(input: TokenStream) -> TokenStream {
66 as_future_impl(TokenStream2::from(input), "implicit_await")
67}
68
69#[proc_macro]
71pub fn as_future_internal(input: TokenStream) -> TokenStream {
72 as_future_impl(TokenStream2::from(input), "crate")
73}
74
75fn as_future_impl(input: TokenStream2, crate_prefix: &'static str) -> TokenStream {
78 let crate_prefix = Ident::new(crate_prefix, Span::call_site());
79 let mut types: Vec<Type> = Vec::new();
80 let mut current_type: TokenStream2 = TokenStream2::new();
81 for token in input {
82 if !is_delimiter(&token) {
83 current_type.extend(vec![token.clone()].drain(..));
84 continue;
85 }
86 let current_type_as_proc_macro_stream = TokenStream::from(current_type);
87 let parsed_type: Type = parse_macro_input!(current_type_as_proc_macro_stream as Type);
88 types.push(parsed_type);
89 current_type = TokenStream2::new();
90 }
91 if !current_type.is_empty() {
92 let current_type_as_proc_macro_stream = TokenStream::from(current_type);
93 let parsed_type: Type = parse_macro_input!(current_type_as_proc_macro_stream as Type);
94 types.push(parsed_type);
95 }
96 let mut output = TokenStream2::new();
97 for impl_type in &types {
98 let generic_args = get_generic_args(&impl_type);
99 output.extend(quote::quote!(
100 impl #generic_args #crate_prefix::as_future::NonFutureAsFuture for #impl_type {
101 fn as_future(self) -> #crate_prefix::as_future::Ready<Self> {
102 #crate_prefix::as_future::ready(self)
103 }
104 }
105 ));
106 }
107 output.into()
108}
109
110fn is_delimiter(token: &TokenTree) -> bool {
111 match token {
112 TokenTree::Punct(punct) => punct.as_char() == ',',
113 _ => false
114 }
115}
116
117fn get_generic_args(impl_type: &Type) -> Option<AngleBracketedGenericArguments> {
118 if let Type::Path(type_path) = impl_type {
119 type_path.path.segments.iter()
120 .filter_map(|path_segment| match &path_segment.arguments {
121 PathArguments::AngleBracketed(args) => Some(args),
122 _ => None
123 })
124 .last()
125 .cloned()
126 } else {
127 None
128 }
129}