use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, parse_quote, DeriveInput, ImplItem, ItemImpl, Visibility, Field, ItemMod, Item, ItemStruct, parse_str};
use util::{field_helpers::extract_fields, mod_helpers::{get_type_name, has_warcmutex_attribute}};
use crate::util::impl_block_helpers::{
change_block_method, get_impl_type_name, remove_pub_from_impl_item,
transform_method_return_type,
};
mod util;
#[proc_macro_attribute]
pub fn warcmutex(_ : TokenStream, input : TokenStream) -> TokenStream{
let item: Item = parse_macro_input!(input as Item);
match item {
Item::Impl(item_impl) => extend_impl(item_impl).into(),
Item::Mod(sub_mod) => extend_mod(sub_mod).into(),
Item::Struct(item_struct ) => extend_struct(item_struct).into(),
_ => panic!("This macro can only be used in structs, impl Blocks and mods!"),
}
}
fn extend_mod(item_mod : ItemMod) -> TokenStream{
let mut tokens = format!("{} mod {}",item_mod.vis.into_token_stream().to_string(),item_mod.ident.to_string());
tokens += "{";
for item in item_mod.content.unwrap().1.iter(){
if !has_warcmutex_attribute(&item){
match item.clone() {
Item::Impl(item_impl) => tokens += &extend_impl(item_impl.clone()).to_string(),
Item::Mod(sub_mod) => tokens += &extend_mod(sub_mod.clone()).to_string(),
Item::Struct(item_struct ) => tokens += &extend_struct(item_struct.clone()).to_string(),
_ => tokens += &item.into_token_stream().to_string(),
}
}
}
tokens += "}";
let output : proc_macro2::TokenStream = parse_str(&tokens).unwrap();
output.into()
}
fn extend_struct(item_struct : ItemStruct) -> TokenStream{
let input: DeriveInput = parse_quote! {
#item_struct
};
let vis = &input.vis;
let field_names = extract_fields(&input);
let field_tokens = field_names.iter().map(|field_name| {
let new_field_name = if let Visibility::Public(_) = field_name.vis{
Field{
attrs: field_name.attrs.clone(),
vis: Visibility::Inherited,
mutability: field_name.mutability.clone(),
ident: field_name.ident.clone(),
colon_token: field_name.colon_token,
ty: field_name.ty.clone(),
}
}else{
field_name.clone()
};
quote! { #new_field_name }
});
let original_struct_name = input.ident.clone();
let base_name = original_struct_name.to_string()+"Base";
let base_name : Ident = parse_str(&base_name).unwrap();
let transformed_struct = quote! {
pub struct #base_name {
#(#field_tokens),*
}
};
let secund_struct = quote! {
#vis struct #original_struct_name {
base: std::sync::Arc<std::sync::Mutex<#base_name>>,
}
};
let lock_impl: ItemImpl = parse_quote!(
impl #original_struct_name{
pub fn lock(&mut self) -> LockResult<MutexGuard<'_, #base_name>> {
self.base.lock()
}
}
);
let clone_impl: ItemImpl = parse_quote!(
impl Clone for #original_struct_name{
fn clone(&self) -> Self {
Self { base: self.base.clone() }
}
}
);
let send_impl: ItemImpl = parse_quote!(
unsafe impl Send for #original_struct_name{}
);
let sync_impl: ItemImpl = parse_quote!(
unsafe impl Sync for #original_struct_name{}
);
let output = quote! {
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::LockResult;
use std::sync::MutexGuard;
#transformed_struct
#secund_struct
#lock_impl
#clone_impl
#send_impl
#sync_impl
};
output.into()
}
fn extend_impl(item_impl : ItemImpl) -> TokenStream{
let original_name = item_impl.self_ty.clone();
let original_name_str = get_type_name( original_name.clone()).expect("Could not get type name.");
let base_name_str = original_name_str+"Base";
let base_name : Ident = parse_str(&base_name_str).unwrap();
let mut original_impl_items = item_impl.items.clone();
original_impl_items
.iter_mut()
.for_each(|ii| transform_method_return_type(ii, get_impl_type_name(&item_impl).unwrap()));
let first_self_ty = parse_quote!(#base_name);
let sencund_self_ty = parse_quote!(#original_name);
let mut first_impl_items: Vec<ImplItem> = vec![];
let mut secund_impl_items: Vec<ImplItem> = vec![];
for original_impl_item in original_impl_items {
if let Some(item) = change_block_method(&original_impl_item,base_name_str.clone()) {
secund_impl_items.push(item);
let mut modificated_impl_item = original_impl_item.clone();
remove_pub_from_impl_item(&mut modificated_impl_item);
first_impl_items.push(modificated_impl_item);
} else {
first_impl_items.push(original_impl_item.clone());
}
}
let secund_impl_block = ItemImpl {
attrs: item_impl.attrs.clone(),
defaultness: item_impl.defaultness,
unsafety: item_impl.unsafety,
impl_token: item_impl.impl_token,
generics: item_impl.generics.clone(),
trait_: item_impl.trait_.clone(),
self_ty: sencund_self_ty,
brace_token: item_impl.brace_token,
items: secund_impl_items,
};
let renamed_impl_block = ItemImpl {
attrs: item_impl.attrs,
defaultness: item_impl.defaultness,
unsafety: item_impl.unsafety,
impl_token: item_impl.impl_token,
generics: item_impl.generics,
trait_: item_impl.trait_,
self_ty: first_self_ty,
brace_token: item_impl.brace_token,
items: first_impl_items,
};
let output = quote! {
#renamed_impl_block
#secund_impl_block
};
output.into()
}