1extern crate proc_macro;
2
3use proc_macro2::{Literal, Span, TokenStream as TokenStream2};
4use quote::quote;
5use syn::{
6 parse_macro_input, Data, DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Ident,
7};
8
9#[proc_macro_derive(Struple)]
10pub fn derive_struple(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
11 let input = parse_macro_input!(input as DeriveInput);
12 expand(input)
13 .unwrap_or_else(|err| err.to_compile_error())
14 .into()
15}
16
17fn expand(
18 DeriveInput {
19 ident: name,
20 generics,
21 data,
22 ..
23 }: DeriveInput,
24) -> syn::Result<TokenStream2> {
25 let fields = match data {
26 Data::Struct(DataStruct { fields, .. }) => fields,
27 _ => {
28 return Err(syn::Error::new(
29 Span::call_site(),
30 "Struple may only be derived on structs",
31 ))
32 }
33 };
34
35 let field_tys = fields.iter().map(|field| &field.ty);
36 let tuple_ty = quote! { ( #( #field_tys, )* ) };
37
38 let (from_tuple_body, into_tuple_body) = make_conversion_impl(&name, fields);
39
40 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
41
42 Ok(quote! {
43 impl #impl_generics ::struple::Struple for #name #ty_generics #where_clause {
44 type Tuple = #tuple_ty;
45 fn from_tuple(tuple: Self::Tuple) -> Self {
46 #from_tuple_body
47 }
48 fn into_tuple(self) -> Self::Tuple {
49 #into_tuple_body
50 }
51 }
52 })
53}
54
55fn make_conversion_impl(name: &Ident, fields: Fields) -> (TokenStream2, TokenStream2) {
56 let tuple_indices = |len| (0..len).map(Literal::usize_unsuffixed);
57 match fields {
58 Fields::Named(FieldsNamed { named, .. }) => (
59 {
60 let indices = tuple_indices(named.len());
61 let fields = named.iter().flat_map(|field| field.ident.as_ref());
62 quote! { #name { #( #fields: tuple.#indices, )* } }
63 },
64 {
65 let fields = named.iter().flat_map(|field| field.ident.as_ref());
66 quote! { ( #( self.#fields, )* ) }
67 },
68 ),
69 Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => (
70 {
71 let indices = tuple_indices(unnamed.len());
72 quote! { #name( #( tuple.#indices, )* ) }
73 },
74 {
75 let indices = tuple_indices(unnamed.len());
76 quote! { ( #( self.#indices, )* ) }
77 },
78 ),
79 Fields::Unit => (quote!(#name), quote!(())),
80 }
81}