use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Attribute, Data, DeriveInput, Fields, Ident};
#[proc_macro_derive(Entity, attributes(dervy))]
pub fn derive_entity(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let id_field = find_id_field(&input.data);
let expanded = quote! {
impl PartialEq for #name {
fn eq(&self, other: &Self) -> bool {
self.#id_field == other.#id_field
}
}
impl Eq for #name {}
impl std::hash::Hash for #name {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.#id_field.hash(state);
}
}
};
TokenStream::from(expanded)
}
fn find_id_field(data: &Data) -> Ident {
if let Data::Struct(data_struct) = data {
if let Fields::Named(fields) = &data_struct.fields {
for field in &fields.named {
if has_dervy_id_attribute(&field.attrs) {
return field.ident.as_ref().unwrap().clone();
}
}
}
}
panic!("No field with #[dervy(id)] attribute found");
}
fn has_dervy_id_attribute(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attr| {
attr.path().is_ident("dervy")
&& attr
.parse_args::<Ident>()
.into_iter()
.any(|ident| ident == "id")
})
}