1extern crate proc_macro;
2
3#[macro_use]
4extern crate quote;
5
6use proc_macro::TokenStream;
7use syn::{DeriveInput, Data, parse_macro_input};
8use syn::{Attribute, Lit, Meta, MetaNameValue};
9use regex::Regex;
10
11#[proc_macro_derive(CustomEq, attributes(ignore_regex))]
12pub fn custom_struct_eq(input: TokenStream) -> TokenStream {
13 let input = parse_macro_input!(input as DeriveInput);
14
15 let re_attr: Option<Attribute> = input.attrs.iter()
17 .find(|attr| {
18 attr.path.is_ident("ignore_regex")
19 })
20 .map(|attr| (*attr).clone());
21
22 let regex_str = if let Some(attribute) = re_attr {
23 let meta = attribute.parse_meta().unwrap();
25
26 if let Meta::NameValue(
28 MetaNameValue { lit: Lit::Str(lit_str), .. }
29 ) = meta {
30 lit_str.value()
31 } else {
32 panic!("Attribute value is not a string literal");
33 }
34 } else {
35 String::from("^$")
37 };
38
39 let regex = Regex::new(®ex_str).expect("Invalid Regex");
40 let name = &input.ident;
41 let mut fields = vec![];
42
43 match input.data {
45 Data::Struct(ref data) => {
46 for field in data.fields.iter() {
47 if let Some(ref ident) = field.ident {
48 let field_name = ident.to_string();
49 if !regex.is_match(&field_name) {
50 fields.push(ident);
51 }
52 }
53 }
54 }
55 _ => {}
56 };
57
58 let output = quote! {
59 impl Eq for #name {}
60 impl PartialEq for #name {
61 fn eq(&self, other: &Self) -> bool {
62 #(self.#fields == other.#fields)&&*
63 }
64 }
65 };
66
67 let stream: TokenStream = output.into();
68 return stream;
69}