struple_impl/
lib.rs

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}