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
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
//! # async-recursion macro
//!
//! [![Build Status](https://travis-ci.org/dcchut/async-recursion.svg?branch=master)](https://travis-ci.org/dcchut/async-recursion)
//! [![codecov](https://codecov.io/gh/dcchut/async-recursion/branch/master/graph/badge.svg)](https://codecov.io/gh/dcchut/async-recursion)
//!
//! Procedural macro for recursive async functions.
//!
//! * [Documentation](https://docs.rs/async-recursion/)
//! * Cargo package: [async-recursion](https://crates.io/crates/async-recursion)
//!
//! ## Motivation
//! Consider the following recursive implementation of the fibonacci numbers:
//!
//! ```rust,ignore
//! 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 function
//! to one returning a boxed Future.
//!
//! ## Example
//!
//! ```rust
//! 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
//!    }
//! }
//! ```
//!
//! ## ?Send Option
//!
//! By default the returned future has a `Send` bound to make sure that it can be sent between threads. If this is not desired you can mark that you would like that that bound to be left out like so:
//!
//! ```rust
//! # use async_recursion::async_recursion;
//! #[async_recursion(?Send)]
//! async fn example() {}
//! ```
//!
//! In other words, `#[async_recursion]` modifies your function to return a [`BoxFuture`] and `#[async_recursion(?Send)]` modifies your function to return a [`LocalBoxFuture`].
//!
//! [`BoxFuture`]: https://docs.rs/futures/0.3.4/futures/future/type.BoxFuture.html
//! [`LocalBoxFuture`]: https://docs.rs/futures/0.3.4/futures/future/type.LocalBoxFuture.html
//!
//! ## Installation
//!
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! async-recursion = "0.2"
//! ```
//!
//! ### 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 expand;
mod parse;

use crate::expand::expand;
use crate::parse::{AsyncItem, RecursionArgs};

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

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

    expand(&mut item, &args);

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