#![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__";
#[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 = "```",
)]
#[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;
{
*block = parse_quote! {
{
#[derive(next_gen::next_gen_hack)]
enum __yield_slot____hack__ {}
#block
}
};
}
{
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;
)}
})
}