extern crate proc_macro2;
use convert_case::{Case, Casing};
use quote::quote;
use syn::{parse_macro_input, Fields};
#[proc_macro_attribute]
pub fn event(
_args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let mut event_enum = parse_macro_input!(input as syn::ItemEnum);
let event_name = &event_enum.ident;
if event_enum.generics.params.len() > 0 {
panic!("HPL Events does not allow generics");
}
let mut func_stream = proc_macro2::TokenStream::default();
event_enum.variants.iter_mut().for_each(|variant| {
let name = &variant.ident;
let formated_name = name
.to_string()
.to_case(Case::Snake)
.parse::<proc_macro2::TokenStream>()
.unwrap();
let mut new_variant: syn::Variant = syn::parse(proc_macro::TokenStream::from({
quote! {
#name {
timestamp: i64
}
}
}))
.unwrap();
let mut func_args_stream = proc_macro2::TokenStream::default();
let mut func_fields_stream = proc_macro2::TokenStream::default();
if let Fields::Named(fileds) = &mut variant.fields {
if let Fields::Named(new_fields) = &mut new_variant.fields {
fileds.named.iter().for_each(|f| {
if let Some(field_name) = &f.ident {
let ty = &f.ty;
func_args_stream.extend::<proc_macro2::TokenStream>(quote! {
#field_name: #ty,
});
func_fields_stream.extend::<proc_macro2::TokenStream>(quote! {
#field_name,
});
}
new_fields.named.push(f.clone())
});
}
}
variant.fields = new_variant.fields;
func_stream.extend::<proc_macro2::TokenStream>(quote! {
pub fn #formated_name(#func_args_stream clock: &Clock) -> Self {
#event_name::#name {
#func_fields_stream
timestamp: clock.unix_timestamp
}
}
});
});
proc_macro::TokenStream::from({
quote! {
#[derive(borsh::BorshSerialize, borsh::BorshDeserialize)]
#event_enum
#[automatically_derived]
impl #event_name {
pub fn to_instruction(&self, wrapper_program_id: Pubkey) -> Instruction {
Instruction {
program_id: wrapper_program_id,
accounts: vec![],
data: vec![
crate::id().try_to_vec().unwrap(),
self.try_to_vec().unwrap(),
]
.concat(),
}
}
pub fn emit_to<'info>(&self, wrapper_program: AccountInfo<'info>) -> ProgramResult {
invoke(
&self.to_instruction(wrapper_program.key()),
&[wrapper_program],
)
}
pub fn emit<'info>(&self, hpl_events_program: AccountInfo<'info>) -> ProgramResult {
if String::from("EventNxhSA3AcXD14PmXaYUiNQWwoKbLeGHtwydixRzX")
!= hpl_events_program.key.to_string()
{
return Err(ProgramError::IncorrectProgramId);
};
self.emit_to(hpl_events_program)
}
#func_stream
}
}
})
}