endify_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{DeriveInput, parse_macro_input};
4
5#[proc_macro_derive(Endify)]
6pub fn endify_derive(input: TokenStream) -> TokenStream {
7    // parse the input tokens into a syntax tree.
8    let input = parse_macro_input!(input as DeriveInput);
9
10    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
11
12    let to_le_impl = gen_method(&input, "to_le");
13    let to_be_impl = gen_method(&input, "to_be");
14    let from_le_impl = gen_method(&input, "from_le");
15    let from_be_impl = gen_method(&input, "from_be");
16
17    let name = &input.ident;
18
19    let expanded = quote! {
20        impl #impl_generics endify::Endify for #name #ty_generics #where_clause {
21                    #to_le_impl
22                    #to_be_impl
23                    #from_le_impl
24                    #from_be_impl
25        }
26    };
27
28    expanded.into()
29}
30
31/// Helper that generates the implementation of a given method.
32fn gen_method(input: &DeriveInput, method_name: &str) -> proc_macro2::TokenStream {
33    let method_ident = syn::Ident::new(method_name, proc_macro2::Span::call_site());
34    match &input.data {
35        syn::Data::Struct(data_struct) => match &data_struct.fields {
36            // named fields
37            syn::Fields::Named(fields) => {
38                let conversions = fields.named.iter().map(|field| {
39                    let ident = field.ident.as_ref().unwrap();
40                    let ty = &field.ty;
41                    quote! {
42                        #ident: #ty::#method_ident(self.#ident)
43                    }
44                });
45                quote! {
46                    fn #method_ident(self) -> Self {
47                        Self {
48                            #(#conversions),*
49                        }
50                    }
51                }
52            }
53            // tuple structs
54            syn::Fields::Unnamed(fields) => {
55                let conversions = fields.unnamed.iter().enumerate().map(|(i, field)| {
56                    let ty = &field.ty;
57                    let index = syn::Index::from(i);
58                    quote! {
59                        #ty::#method_ident(self.#index)
60                    }
61                });
62                quote! {
63                    fn #method_ident(self) -> Self {
64                        Self (
65                            #(#conversions),*
66                        )
67                    }
68                }
69            }
70            // unit structs
71            syn::Fields::Unit => {
72                quote! {
73                    fn #method_ident(self) -> Self {
74                        self
75                    }
76                }
77            }
78        },
79        _ => unimplemented!("only structs are supported right now. Feel free to open a PR."),
80    }
81}