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
extern crate proc_macro; use proc_macro2::{Literal, Span, TokenStream as TokenStream2}; use quote::quote; use syn::{ parse_macro_input, Data, DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Ident, }; #[proc_macro_derive(Struple)] pub fn derive_struple(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); expand(input) .unwrap_or_else(|err| err.to_compile_error()) .into() } fn expand( DeriveInput { ident: name, generics, data, .. }: DeriveInput, ) -> syn::Result<TokenStream2> { let fields = match data { Data::Struct(DataStruct { fields, .. }) => fields, _ => { return Err(syn::Error::new( Span::call_site(), "Struple may only be derived on structs", )) } }; let field_tys = fields.iter().map(|field| &field.ty); let tuple_ty = quote! { ( #( #field_tys, )* ) }; let (from_tuple_body, into_tuple_body) = make_conversion_impl(&name, fields); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); Ok(quote! { impl #impl_generics ::struple::Struple for #name #ty_generics #where_clause { type Tuple = #tuple_ty; fn from_tuple(tuple: Self::Tuple) -> Self { #from_tuple_body } fn into_tuple(self) -> Self::Tuple { #into_tuple_body } } }) } fn make_conversion_impl(name: &Ident, fields: Fields) -> (TokenStream2, TokenStream2) { let tuple_indices = |len| (0..len).map(Literal::usize_unsuffixed); match fields { Fields::Named(FieldsNamed { named, .. }) => ( { let indices = tuple_indices(named.len()); let fields = named.iter().flat_map(|field| field.ident.as_ref()); quote! { #name { #( #fields: tuple.#indices, )* } } }, { let fields = named.iter().flat_map(|field| field.ident.as_ref()); quote! { ( #( self.#fields, )* ) } }, ), Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => ( { let indices = tuple_indices(unnamed.len()); quote! { #name( #( tuple.#indices, )* ) } }, { let indices = tuple_indices(unnamed.len()); quote! { ( #( self.#indices, )* ) } }, ), Fields::Unit => (quote!(#name), quote!(())), } }