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
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{self, parse_macro_input, parse_quote};

#[proc_macro_derive(DynPartialEq)]
pub fn dyn_partial_eq_macro_derive(input: TokenStream) -> TokenStream {
  let ast = syn::parse(input).unwrap();

  impl_dyn_partial_eq(&ast)
}

fn impl_dyn_partial_eq(ast: &syn::DeriveInput) -> TokenStream {
  let name = &ast.ident;
  let gen = quote! {
      impl DynPartialEq for #name {
          fn as_any(&self) -> &dyn std::any::Any {
            self
          }
          fn box_eq(&self, other: &dyn std::any::Any) -> bool {
            other.downcast_ref::<Self>().map_or(false, |a| self == a)
          }
      }
  };
  gen.into()
}


#[proc_macro_attribute]
pub fn dyn_partial_eq(_: TokenStream, input: TokenStream) -> TokenStream {
  let mut input = parse_macro_input!(input as syn::ItemTrait);

  let name = &input.ident;

  let bound: syn::TypeParamBound = parse_quote! {
    dyn_partial_eq::DynPartialEq
  };

  input.supertraits.push(bound);

  (quote! {
    #input

    impl core::cmp::PartialEq for Box<dyn #name> {
      fn eq(&self, other: &Box<dyn #name>) -> bool {
        self.box_eq(other.as_any())
      }
    }
  }).into()
}