1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//! <h5>Procedural macro for recursive async functions</h5>
//!
//! Consider the following recursive implementation of the fibonacci numbers:
//!
//! ```compile_fail
//! async fn fib(n : u32) -> u64 {
//!    match n {
//!        0     => panic!("zero is not a valid argument to fib()!"),
//!        1 | 2 => 1,
//!        3     => 2,
//!        _ => fib(n-1).await + fib(n-2).await
//!    }
//! }
//! ```
//!
//! The compiler helpfully tells us that:
//!
//! ```console
//! error[E0733]: recursion in an `async fn` requires boxing
//! --> src/main.rs:1:26
//! |
//! 1 | async fn fib(n : u32) -> u64 {
//! |                          ^^^ recursive `async fn`
//! |
//! = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`.
//! ```
//!
//! This crate provides an attribute macro to automatically convert an async recursive function
//! to one returning a boxed Future.
//!
//! This crate provides an attribute macro to automatically convert async fn f(...) -> ReturnType
//! to a fn f(...) -> Pin<Box<dyn Future<Output = ReturnType> + Send>>
//!
//! # Example
//!
//! ```
//! use async_recursion::async_recursion;
//!
//! #[async_recursion]
//! async fn fib(n : u32) -> u64 {
//!    match n {
//!        0     => panic!("zero is not a valid argument to fib()!"),
//!        1 | 2 => 1,
//!        3     => 2,
//!        _ => fib(n-1).await + fib(n-2).await
//!    }
//! }
//! ```
//!
//! # Limitations
//! Currently the macro doesn't consider lifetimes at all; this is something I plan to work
//! on in the future.
//!
//! ### License
//! Licensed under either of
//! * Apache License, Version 2.0
//!   ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
//! * MIT license
//!   ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
//! at your option.

extern crate proc_macro;

mod parse;
mod expand;

use crate::parse::AsyncItem;
use crate::expand::expand;

use proc_macro::TokenStream;
use syn::parse_macro_input;
use quote::quote;

#[proc_macro_attribute]
pub fn async_recursion(_args: TokenStream, input: TokenStream) -> TokenStream {
    let mut item = parse_macro_input!(input as AsyncItem);

    expand(&mut item);

    TokenStream::from(quote!(#item))
}