lombok 0.4.0

Lombok port for Rust
Documentation
use proc_macro::TokenStream;

use quote::{format_ident, quote};

use crate::utils::syn::{named_fields, parse_derive_input};

pub(crate) fn equals_and_hash_code(input: TokenStream) -> TokenStream {
    let derive_input = parse_derive_input(input);

    let name = &derive_input.ident;
    let (impl_generics, ty_generics, where_clause) = &derive_input.generics.split_for_impl();
    let fields = named_fields(&derive_input);

    let struct_refs_first = fields.iter().enumerate().map(|(i, field)| {
        let field_name = field.ident.clone().unwrap();
        let reference = format_ident!("__self_1_{}", i.to_string());

        quote! {
            #field_name: ref #reference,
        }
    });
    let struct_refs_second = fields.iter().enumerate().map(|(i, field)| {
        let field_name = field.ident.clone().unwrap();
        let reference = format_ident!("__self_0_{}", i.to_string());

        quote! {
            #field_name: ref #reference,
        }
    });

    let struct_refs_second_eq = struct_refs_second.clone();
    let struct_refs_first_eq = struct_refs_first.clone();
    let struct_refs_second_ne = struct_refs_second.clone();
    let struct_refs_first_ne = struct_refs_first.clone();

    let equalities = fields.iter().enumerate().map(|(i, _)| {
        let reference_first = format_ident!("__self_0_{}", i.to_string());
        let reference_second = format_ident!("__self_1_{}", i.to_string());

        quote! {
            (*#reference_first) == (*#reference_second) &&
        }
    });
    let non_equalities = fields.iter().enumerate().map(|(i, _)| {
        let reference_first = format_ident!("__self_0_{}", i.to_string());
        let reference_second = format_ident!("__self_1_{}", i.to_string());

        quote! {
            (*#reference_first) != (*#reference_second) ||
        }
    });

    let hashes = fields.iter().enumerate().map(|(i, _)| {
        let reference = format_ident!("__self_0_{}", i.to_string());

        quote! {
             ::core::hash::Hash::hash(&(*#reference), state);
        }
    });

    TokenStream::from(quote! {
        impl #impl_generics #name #ty_generics #where_clause {
            fn equals(&self, other: &#name #ty_generics) -> bool {
                self.eq(&other)
            }

            fn hash_code(&self) -> u64 {
                let mut hasher = ::std::collections::hash_map::DefaultHasher::new();
                ::std::hash::Hash::hash(&self, &mut hasher);
                ::std::hash::Hasher::finish(&hasher)
            }
        }

        impl #impl_generics ::core::cmp::PartialEq for #name #ty_generics #where_clause {
            fn eq(&self, other: &#name #ty_generics) -> bool {
                match *other {
                    #name {
                        #(
                            #struct_refs_second_eq
                        )*
                    } => match *self {
                        #name {
                            #(
                                #struct_refs_first_eq
                            )*
                        } => {
                            #(
                                #equalities
                            )* true
                        }
                    },
                }
            }

            fn ne(&self, other: &#name #ty_generics) -> bool {
                match *other {
                    #name {
                        #(
                            #struct_refs_second_ne
                        )*
                    } => match *self {
                        #name {
                            #(
                                #struct_refs_first_ne
                            )*
                        } => {
                            #(
                                #non_equalities
                            )* false
                        }
                    },
                }
            }
        }

        impl #impl_generics ::core::hash::Hash for #name #ty_generics #where_clause {
            fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
                match *self {
                    #name {
                        #(
                            #struct_refs_second
                        )*
                    } => {
                        #(
                            #hashes
                        )*
                    }
                }
            }
        }
    })
}