1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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
                    // name: u8
                }
            }
        }))
        .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 {
                        // if let () = f.ty {}
                        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 wrap(&self) -> Vec<u8> {
                    vec![
                        crate::id().try_to_vec().unwrap(),
                        self.try_to_vec().unwrap(),
                    ].concat()
                }

                pub fn emit<'info>(&self, noop: &Program<'info, Noop>) -> Result<()> {
                    wrap_application_data_v1(
                        self.wrap(),
                        noop,
                    )
                }

                // pub fn to_instruction(&self, wrapper_program_id: Pubkey) -> Instruction {
                //     Instruction {
                //         program_id: wrapper_program_id,
                //         accounts: vec![],
                //         data: self.wrap(),
                //     }
                // }

                // 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

            }
        }
    })
}