use proc_macro2::Span; use quote::{quote, ToTokens}; use syn::{Attribute, Ident}; use crate::attribute_token_type::*; const TRANSACTION_STRING: &'static str = "transaction"; const TRACKER_STRING: &'static str = "tracker"; #[derive(Debug, PartialEq)] pub struct AttributeInfo { pub ident: Ident, pub token_type: Option, } impl AttributeInfo { pub fn from_attribute(attribute: &Attribute) -> Option { let attribute_idents: Vec = attribute .path() .segments .iter() .map(|segment| segment.ident.clone()) .collect(); assert!( attribute_idents.len() == 1, "only one ident per attribute is allowed" ); match attribute_idents[0].to_string().as_str() { TRANSACTION_STRING => (), TRACKER_STRING => (), // filter attributes not affiliated with this crate _ => return None, } let token_type = if let syn::Meta::List(_) = &attribute.meta { Some(attribute.parse_args().expect("failed parsing attribute")) } else { None }; Some(Self { ident: attribute_idents[0].clone(), token_type, }) } pub fn is_tracker(infos: &Vec) -> bool { infos .iter() .find(|info| info.ident == Ident::new(TRACKER_STRING, Span::call_site())) .is_some() } pub fn is_transaction(infos: &Vec) -> bool { infos .iter() .find(|info| info.ident == Ident::new(TRANSACTION_STRING, Span::call_site())) .is_some() } pub fn find_pre_setter_exec_function(infos: &Vec) -> Option<&Ident> { for info in infos.iter() { if let Some(token_type) = &info.token_type { match token_type { AttributeTokenType::PreSetterExec(exec_function) => return Some(exec_function), _ => (), } } } None } pub fn find_setter_exec_function(infos: &Vec) -> Option<&Ident> { for info in infos.iter() { if let Some(token_type) = &info.token_type { match token_type { AttributeTokenType::SetterExec(exec_function) => return Some(exec_function), _ => (), } } } None } pub fn find_apply_exec_function(infos: &Vec) -> Option<&Ident> { for info in infos.iter() { if let Some(token_type) = &info.token_type { match token_type { AttributeTokenType::ApplyExec(exec_function) => return Some(exec_function), _ => (), } } } None } pub fn find_convert_type(infos: &Vec) -> Option<&proc_macro2::TokenStream> { for info in infos.iter() { if let Some(token_type) = &info.token_type { match token_type { AttributeTokenType::ConvertAs(convert_type) => return Some(convert_type), _ => (), } } } None } pub fn find_skip_getter(infos: &Vec) -> bool { for info in infos.iter() { if let Some(token_type) = &info.token_type { match token_type { AttributeTokenType::SkipGetter => return true, _ => (), } } } false } pub fn find_custom_from(infos: &Vec) -> Option<&Ident> { for info in infos.iter() { if let Some(token_type) = &info.token_type { match token_type { AttributeTokenType::CustomFrom(custom_from) => return Some(custom_from), _ => (), } } } None } pub fn find_custom_into(infos: &Vec) -> Option<&Ident> { for info in infos.iter() { if let Some(token_type) = &info.token_type { match token_type { AttributeTokenType::CustomInto(custom_into) => return Some(custom_into), _ => (), } } } None } pub fn find_compile_feature(infos: &Vec) -> Option<&proc_macro2::TokenStream> { for info in infos.iter() { if let Some(token_type) = &info.token_type { match token_type { AttributeTokenType::CompileFeature(feature) => return Some(feature), _ => (), } } } None } pub fn find_private_setter(infos: &Vec) -> bool { for info in infos.iter() { if let Some(token_type) = &info.token_type { match token_type { AttributeTokenType::PrivateSetter => return true, _ => (), } } } false } pub fn find_compile_not_feature( infos: &Vec, ) -> Option<&proc_macro2::TokenStream> { for info in infos.iter() { if let Some(token_type) = &info.token_type { match token_type { AttributeTokenType::CompileNotFeature(feature) => return Some(feature), _ => (), } } } None } pub fn set_features(infos: &Vec, tokens: &mut proc_macro2::TokenStream) { // check features if let Some(feature) = Self::find_compile_feature(infos) { proc_macro2::TokenStream::from(quote! { #[cfg(feature = #feature)] }) .to_tokens(tokens); } if let Some(feature) = Self::find_compile_not_feature(infos) { proc_macro2::TokenStream::from(quote! { #[cfg(not(feature = #feature))] }) .to_tokens(tokens); } } pub fn check_duplicate(attributes: &Vec) { let mut visited: Vec<&AttributeInfo> = Vec::new(); for attribute in attributes.iter() { match visited .iter() .find(|visited_attribute| ***visited_attribute == *attribute) { Some(_) => panic!("Attribute was used multiple times {}", attribute.ident), None => visited.push(&attribute), } } } }