next-gen_proc-macro 0.0.5

Safe generators on stable Rust.
Documentation
#![cfg_attr(feature = "external_doc",
    feature(external_doc),
)]
#![doc(test(attr(deny(warnings))))]

#[macro_use]
extern crate fstrings;
extern crate proc_macro;

use ::proc_macro::TokenStream;
use quote::{
    quote,
    quote_spanned,
};
use ::syn::{*,
    spanned::Spanned,
};

#[macro_use]
mod macros;

const IDENT_SUFFIX: &'static str = "__hack__";
/// Transforms a function with `yield_!` calls into a generator.
#[cfg_attr(feature = "external_doc",
    doc = "",
    doc = "# Example",
    doc = "",
    doc = "```rust",
    doc = "# macro_rules! ignore {($($tt:tt)*) => ()} ignore! {",
    doc(include = "doc_examples/generator.rs"),
    doc = "# }",
    doc = "```",
)]
///
/// # Expansion
///
/// The above example expands to:
#[cfg_attr(feature = "external_doc",
    doc = "",
    doc = "```rust",
    doc = "# macro_rules! ignore {($($tt:tt)*) => ()} ignore! {",
    doc(include = "doc_examples/generator_desugared.rs"),
    doc = "# }",
    doc = "```",
)]
#[proc_macro_attribute] pub
fn generator (params: TokenStream, input: TokenStream)
  -> TokenStream
{
    #[allow(unused)]
    const FUNCTION_NAME: &str = "generator";

    let yield_type: Type = parse_macro_input!(params as Type);

    debug_input!(&input);
    let mut function: ItemFn = parse_macro_input!(input);
    let ItemFn {
        ref mut block,
        ref mut sig,
        ..
    } = function;

    // Update block to generate `yield_!` macro.
    {
        *block = parse_quote! {
            {
                #[derive(next_gen::next_gen_hack)]
                enum __yield_slot____hack__ {}

                #block
            }
        };
    }

    // Handle the signature
    {
        sig.asyncness = parse_quote! { async };

        match sig
                .inputs
                .iter()
                .find(|&fn_arg| match *fn_arg {
                    | FnArg::Receiver(_) => true,
                    | _ => false,
                })
        {
            | Some(ty) => return {
                Error::new(ty.span(), &f!(
                    "`#[{FUNCTION_NAME}]` does not support `self` receivers yet"
                )).to_compile_error().into()
            },

            | _ => {},
        }

        let (pats, tys): (Vec<_>, Vec<_>) =
            ::core::mem::replace(&mut sig.inputs, Default::default())
                .into_iter()
                .map(|fn_arg| match fn_arg {
                    | FnArg::Receiver(_) => unreachable!(),
                    | FnArg::Typed(PatType { pat, ty, .. }) => (pat, ty),
                })
                .unzip()
        ;
        sig.inputs = parse_quote! {
            __yield_slot__: next_gen::YieldSlot<'_, #yield_type>,
            ( #(#pats ,)* ): ( #(#tys ,)* ),
        };
    }

    TokenStream::from(debug_output!(quote! {
        #function
    }))
}


#[doc(hidden)]
#[proc_macro_derive(next_gen_hack)] pub
fn hack (input: TokenStream) -> TokenStream
{
    let input: DeriveInput = parse_macro_input!(input);
    let ident: String = input.ident.to_string();
    let ident: &str = &ident[.. ident.len() - IDENT_SUFFIX.len()];
    let yield_slot = Ident::new(ident, input.ident.span());
    TokenStream::from(quote_spanned! { input.span() =>
        macro_rules! yield_ {(
            $value:expr
        ) => (
            let () = #yield_slot.put($value).await;
        )}
    })
}