use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::parse_macro_input;
pub(crate) fn fields_idents(
fields: impl Iterator<Item = syn::Field>,
) -> impl Iterator<Item = proc_macro2::TokenStream> {
fields.enumerate().map(|(ix, field)| {
field.ident.map(|i| quote! {#i}).unwrap_or_else(|| {
let f_ix: syn::Ident = syn::Ident::new(&format!("f_{}", ix), Span::call_site());
quote!( #f_ix )
})
})
}
pub(crate) fn fields_access(
fields: impl Iterator<Item = syn::Field>,
) -> impl Iterator<Item = proc_macro2::TokenStream> {
fields.enumerate().map(|(ix, field)| {
field.ident.map(|i| quote!( #i )).unwrap_or_else(|| {
let f_ix: syn::Index = syn::Index { index: ix as u32, span: Span::call_site() };
quote!( #f_ix )
})
})
}
#[proc_macro_derive(Parse)]
pub fn derive_parse(input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as syn::Item);
match item {
syn::Item::Struct(input) => derive_parse_struct(input),
_ => TokenStream::new(), }
}
fn derive_parse_struct(input: syn::ItemStruct) -> TokenStream {
let syn::ItemStruct { ident, generics, fields, .. } = input;
let field_names = {
let name = fields_idents(fields.iter().map(Clone::clone));
quote! {
#(
#name,
)*
}
};
let field = fields_idents(fields.iter().map(Clone::clone));
let tokens = quote! {
impl #generics syn::parse::Parse for #ident #generics {
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
#(
let #field = input.parse()?;
)*
Ok(Self {
#field_names
})
}
}
};
tokens.into()
}
#[proc_macro_derive(ToTokens)]
pub fn derive_totokens(input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as syn::Item);
match item {
syn::Item::Enum(input) => derive_totokens_enum(input),
syn::Item::Struct(input) => derive_totokens_struct(input),
_ => TokenStream::new(), }
}
fn derive_totokens_struct(input: syn::ItemStruct) -> TokenStream {
let syn::ItemStruct { ident, generics, fields, .. } = input;
let fields = fields_access(fields.iter().map(Clone::clone));
let tokens = quote! {
impl #generics quote::ToTokens for #ident #generics {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
#(
self.#fields.to_tokens(tokens);
)*
}
}
};
tokens.into()
}
fn derive_totokens_enum(input: syn::ItemEnum) -> TokenStream {
let syn::ItemEnum { ident, generics, variants, .. } = input;
let variants = variants.iter().map(|v| {
let v_ident = v.ident.clone();
let fields_build = if v.fields.iter().count() > 0 {
let fields_id = fields_idents(v.fields.iter().map(Clone::clone));
quote!( (#(#fields_id), *) )
} else {
quote!()
};
let field = fields_idents(v.fields.iter().map(Clone::clone));
quote! {
#ident::#v_ident #fields_build => {
#(
#field.to_tokens(tokens);
)*
},
}
});
let tokens = quote! {
impl #generics quote::ToTokens for #ident #generics {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
#(
#variants
)*
}
}
}
};
tokens.into()
}