extern crate proc_macro;
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::{parse, punctuated::Punctuated, Attribute, Data, DeriveInput, Fields, Meta, Token};
#[proc_macro_attribute]
pub fn typeshare(_attr: TokenStream, item: TokenStream) -> TokenStream {
if let Ok(mut item) = parse::<DeriveInput>(item.clone()) {
strip_configuration_attribute(&mut item);
TokenStream::from(item.to_token_stream())
} else {
item
}
}
const CONFIG_ATTRIBUTE_NAME: &str = "typeshare";
fn is_typeshare_attribute(attribute: &Attribute) -> bool {
let has_cfg_attr = || {
if attribute.path().is_ident("cfg_attr") {
if let Ok(meta) =
attribute.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
{
return meta.into_iter().any(
|meta| matches!(meta, Meta::List(meta_list) if meta_list.path.is_ident(CONFIG_ATTRIBUTE_NAME)),
);
}
}
false
};
attribute.path().is_ident(CONFIG_ATTRIBUTE_NAME) || has_cfg_attr()
}
fn strip_configuration_attribute(item: &mut DeriveInput) {
fn remove_configuration_from_attributes(attributes: &mut Vec<Attribute>) {
attributes.retain(|attribute| !is_typeshare_attribute(attribute));
}
fn remove_configuration_from_fields(fields: &mut Fields) {
for field in fields.iter_mut() {
remove_configuration_from_attributes(&mut field.attrs);
}
}
match item.data {
Data::Enum(ref mut data_enum) => {
for variant in data_enum.variants.iter_mut() {
remove_configuration_from_attributes(&mut variant.attrs);
remove_configuration_from_fields(&mut variant.fields);
}
}
Data::Struct(ref mut data_struct) => {
remove_configuration_from_fields(&mut data_struct.fields);
}
Data::Union(ref mut data_union) => {
for field in data_union.fields.named.iter_mut() {
remove_configuration_from_attributes(&mut field.attrs);
}
}
};
}