struct_macro_eq/
lib.rs

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    // Collect regex pattern from attribute or set to default value
16    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        // Parse the attribute into a Meta object
24        let meta = attribute.parse_meta().unwrap();
25
26        // Extract the attribute value from the Meta object
27        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        // Use a catch-none regex if the attribute is not present
36        String::from("^$")
37    };
38
39    let regex = Regex::new(&regex_str).expect("Invalid Regex");
40    let name = &input.ident;
41    let mut fields = vec![];
42
43    // collect all struct field names that don't match the regex
44    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}