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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
extern crate proc_macro;

use quote::quote;
use syn::parse_macro_input;
use syn::{parse::Parse, Expr, Token};

struct MacroInput2 {
    arg_1: Expr,
    comma: Token![,],
    arg_2: Expr,
}

impl Parse for MacroInput2 {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        Ok(Self {
            arg_1: input.parse()?,
            comma: input.parse()?,
            arg_2: input.parse()?,
        })
    }
}

#[proc_macro_derive(SchemaGeneratorAnchor)]
pub fn schema_generator_anchor(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let account_strct = parse_macro_input!(input as syn::ItemStruct);

    let account_name = &account_strct.ident;

    proc_macro::TokenStream::from(quote! {
        impl solalumin_state::states::SchemaEventAnchor for #account_name {
             fn generate_schema(self) -> Vec<u8> {
                let mut d = Self::DISCRIMINATOR.to_vec();
                let mut defs = Default::default();
                Self::add_definitions_recursively(&mut defs);
                let container: borsh::schema::BorshSchemaContainer = Self::schema_container();
                let mut data = container
                    .try_to_vec()
                    .expect("Failed to serialize BorshSchemaContainer for account");
                d.append(&mut data);
                d
            }
        }
    })
}

#[proc_macro_attribute]
pub fn schema_generator(
    _args: proc_macro::TokenStream,
    input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let event_strct = parse_macro_input!(input as syn::ItemStruct);

    let event_name = &event_strct.ident;

    let discriminator: proc_macro2::TokenStream = {
        let discriminator_preimage = format!("event:{event_name}");
        let mut discriminator = [0u8; 8];
        discriminator.copy_from_slice(
            &solalumin_state::solalumin_hash::hash(discriminator_preimage.as_bytes()).to_bytes()[..8],
        );
        format!("{discriminator:?}").parse().unwrap()
    };

    proc_macro::TokenStream::from(quote! {
        #[derive(borsh::BorshSerialize, borsh::BorshDeserialize, borsh::BorshSchema)]
        #event_strct

        impl solalumin_state::states::SchemaEvent for #event_name {
            fn data(&self) -> Vec<u8> {
                let mut d = #discriminator.to_vec();
                d.append(&mut self.try_to_vec().unwrap());
                d
            }

            fn generate_schema(self) -> Vec<u8> {
                let mut d = #discriminator.to_vec();
                let mut defs = Default::default();
                Self::add_definitions_recursively(&mut defs);
                let container: borsh::schema::BorshSchemaContainer = Self::schema_container();
                let mut data = container
                    .try_to_vec()
                    .expect("Failed to serialize BorshSchemaContainer for event");
                d.append(&mut data);
                d
            }
        }

        impl solalumin_state::states::Discriminator for #event_name {
            const DISCRIMINATOR: [u8; 8] = #discriminator;
        }
    })
}

#[proc_macro]
pub fn register_program_log_data_schema(
    token_stream: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let input = parse_macro_input!(token_stream as MacroInput2);

    let data = &input.arg_1;
    let is_anchor = &input.arg_2;
    proc_macro::TokenStream::from(quote! {
        {
            let schema_string = base64::encode(#data.generate_schema().as_slice());
            msg!("LD:{}:{}", #is_anchor, schema_string);
        }
    })
}

#[proc_macro]
pub fn register_program_account_schema(
    token_stream: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let input = parse_macro_input!(token_stream as MacroInput2);

    let data = &input.arg_1;
    let is_anchor = &input.arg_2;
    proc_macro::TokenStream::from(quote! {
        {
            let schema_string = base64::encode(#data.generate_schema().as_slice());
            msg!("AR:{}:{}", #is_anchor, schema_string);
        }
    })
}

#[proc_macro]
pub fn register_program_instruction_log_schema(
    input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let data: proc_macro2::TokenStream = input.into();
    proc_macro::TokenStream::from(quote! {
        {
            solana_program::log::sol_log_data(&[&#data.generate_schema()]);
        }
    })
}

#[proc_macro]
pub fn publish(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let data: proc_macro2::TokenStream = input.into();
    proc_macro::TokenStream::from(quote! {
        {
            solana_program::log::sol_log_data(&[&solalumin_state::states::SchemaEvent::data(&#data)]);
        }
    })
}