#![recursion_limit = "512"]
use proc_macro2::{Ident, Span};
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{parse_macro_input, Data, DeriveInput, Fields, Index};
#[proc_macro_derive(destructure)]
pub fn derive_destructure(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let output = match input.data {
Data::Struct(ref data) => {
match data.fields {
Fields::Named(ref fields) => {
let field_types = fields.named.iter().map(|f| {
let ty = &f.ty;
quote_spanned! {f.span()=>
#ty
}
});
let field_reads = fields.named.iter().map(|f| {
let ident = &f.ident;
quote_spanned! {f.span()=>
::core::ptr::read(&self_ref.#ident)
}
});
quote! {
impl #impl_generics #name #ty_generics #where_clause {
#[inline(always)]
fn destructure(self) -> (#(#field_types,)*) {
let maybe_uninit = ::core::mem::MaybeUninit::new(self);
unsafe {
let self_ref = &*maybe_uninit.as_ptr();
(#(#field_reads,)*)
}
}
}
}
}
Fields::Unnamed(ref fields) => {
let field_types = fields.unnamed.iter().map(|f| {
let ty = &f.ty;
quote_spanned! {f.span()=>
#ty
}
});
let field_reads = fields.unnamed.iter().enumerate().map(|(i,f)| {
let index = Index::from(i);
quote_spanned! {f.span()=>
::core::ptr::read(&self_ref.#index)
}
});
quote! {
impl #impl_generics #name #ty_generics #where_clause {
#[inline(always)]
fn destructure(self) -> (#(#field_types,)*) {
let maybe_uninit = ::core::mem::MaybeUninit::new(self);
unsafe {
let self_ref = &*maybe_uninit.as_ptr();
(#(#field_reads,)*)
}
}
}
}
}
Fields::Unit => {
quote! {
impl #impl_generics #name #ty_generics #where_clause {
#[inline(always)]
fn destructure(self) {
let _ = ::core::mem::MaybeUninit::new(self);
}
}
}
}
}
}
Data::Enum(_) => panic!("#[derive(destructure)] doesn't work on enums, use #[derive(remove_trait_impls)] instead."),
Data::Union(_) => panic!("#[derive(destructure)] doesn't work on unions."),
};
proc_macro::TokenStream::from(output)
}
#[proc_macro_derive(remove_trait_impls)]
pub fn derive_remove_trait_impls(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let new_type_name = Ident::new(&(name.to_string() + "WithoutTraitImpls"), Span::call_site());
let output = match input.data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
let fields_iter = fields.named.iter().map(|f| {
let ident = &f.ident;
let ty = &f.ty;
quote_spanned! {f.span()=>
#ident: #ty
}
});
let field_reads_iter = fields.named.iter().map(|f| {
let ident = &f.ident;
quote_spanned! {f.span()=>
#ident: ::core::ptr::read(&self_ref.#ident)
}
});
quote! {
#[must_use]
struct #new_type_name #ty_generics #where_clause {
#(#fields_iter,)*
}
impl #impl_generics #name #ty_generics #where_clause {
#[inline(always)]
fn remove_trait_impls(self) -> #new_type_name #ty_generics {
let maybe_uninit = ::core::mem::MaybeUninit::new(self);
unsafe {
let self_ref = &*maybe_uninit.as_ptr();
#new_type_name {
#(#field_reads_iter,)*
}
}
}
}
}
}
Fields::Unnamed(ref fields) => {
let fields_iter = fields.unnamed.iter().map(|f| {
let ty = &f.ty;
quote_spanned! {f.span()=>
#ty
}
});
let field_reads_iter = fields.unnamed.iter().enumerate().map(|(i, f)| {
let index = Index::from(i);
quote_spanned! {f.span()=>
::core::ptr::read(&self_ref.#index)
}
});
quote! {
#[must_use]
struct #new_type_name #ty_generics #where_clause (#(#fields_iter,)*);
impl #impl_generics #name #ty_generics #where_clause {
#[inline(always)]
fn remove_trait_impls(self) -> #new_type_name #ty_generics {
let maybe_uninit = ::core::mem::MaybeUninit::new(self);
unsafe {
let self_ref = &*maybe_uninit.as_ptr();
#new_type_name(#(#field_reads_iter,)*)
}
}
}
}
}
Fields::Unit => {
quote! {
#[must_use]
struct #new_type_name #ty_generics #where_clause;
impl #impl_generics #name #ty_generics #where_clause {
#[inline(always)]
fn remove_trait_impls(self) -> #new_type_name #ty_generics {
let _ = ::core::mem::MaybeUninit::new(self);
#new_type_name
}
}
}
}
},
Data::Enum(ref data) => {
let variants_iter = data.variants.iter().map(|variant| {
let variant_ident = &variant.ident;
match variant.fields {
Fields::Named(ref fields) => {
let fields_iter = fields.named.iter().map(|f| {
let ident = &f.ident;
let ty = &f.ty;
quote_spanned! {f.span()=>
#ident: #ty
}
});
quote! {
#variant_ident {
#(#fields_iter,)*
}
}
}
Fields::Unnamed(ref fields) => {
let fields_iter = fields.unnamed.iter().map(|f| {
let ty = &f.ty;
quote_spanned! {f.span()=>
#ty
}
});
quote! {
#variant_ident(#(#fields_iter,)*)
}
}
Fields::Unit => {
quote!(#variant_ident)
}
}
});
let match_arms_iter = data.variants.iter().map(|variant| {
let variant_ident = &variant.ident;
match variant.fields {
Fields::Named(ref fields) => {
let fields_iter = fields.named.iter().map(|f| {
let ident = &f.ident;
quote_spanned! {f.span()=>
ref #ident
}
});
let field_reads_iter = fields.named.iter().map(|f| {
let ident = &f.ident;
quote_spanned! {f.span()=>
#ident: ::core::ptr::read(#ident)
}
});
quote! {
#name::#variant_ident { #(#fields_iter,)* } => #new_type_name::#variant_ident { #(#field_reads_iter,)* }
}
}
Fields::Unnamed(ref fields) => {
let fields_iter = fields.unnamed.iter().enumerate().map(|(i,f)| {
let index = Ident::new(&format!("__{}", i), f.span());
quote_spanned! {f.span()=>
ref #index
}
});
let field_reads_iter = fields.unnamed.iter().enumerate().map(|(i,f)| {
let index = Ident::new(&format!("__{}", i), f.span());
quote_spanned! {f.span()=>
::core::ptr::read(#index)
}
});
quote! {
#name::#variant_ident(#(#fields_iter,)*) => #new_type_name::#variant_ident(#(#field_reads_iter,)*)
}
}
Fields::Unit => {
quote!{
#name::#variant_ident => #new_type_name::#variant_ident
}
}
}
});
quote! {
enum #new_type_name #ty_generics #where_clause {
#(#variants_iter,)*
}
impl #impl_generics #name #ty_generics #where_clause {
#[inline(always)]
fn remove_trait_impls(self) -> #new_type_name #ty_generics {
let maybe_uninit = ::core::mem::MaybeUninit::new(self);
unsafe {
match &*maybe_uninit.as_ptr() {
#(#match_arms_iter,)*
}
}
}
}
}
}
Data::Union(_) => panic!("#[derive(remove_trait_impls)] doesn't work on unions."),
};
proc_macro::TokenStream::from(output)
}