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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
extern crate proc_macro;

use proc_macro::TokenStream;

use quote::quote;
use syn::parse_macro_input;
use syn::DeriveInput;

use charybdis_parser::fields::CharybdisFields;
use charybdis_parser::traits::CharybdisMacroArgs;

use crate::model::*;
use crate::native::{
    decrement_counter_methods, delete_by_primary_key_functions, find_by_global_secondary_index,
    find_by_local_secondary_index, find_by_primary_keys_functions, increment_counter_methods,
    pull_from_collection_consts, pull_from_collection_consts_if_exists, pull_from_collection_methods,
    pull_from_collection_methods_if_exists, push_to_collection_consts, push_to_collection_consts_if_exists,
    push_to_collection_methods, push_to_collection_methods_if_exists,
};
use crate::rules::*;
use crate::scylla::from_row;

mod model;
mod native;
mod rules;

mod scylla;
mod traits;

#[proc_macro_attribute]
pub fn charybdis_model(args: TokenStream, input: TokenStream) -> TokenStream {
    let args: CharybdisMacroArgs = parse_macro_input!(args);

    let mut input: DeriveInput = parse_macro_input!(input);
    let mut new_fields = CharybdisFields::from_input(&input, &args);
    let fields = new_fields.populate(&args);

    let struct_name = &input.ident.clone();

    // partial_<model_name>!(StructName, field1, field2, ...);
    let partial_model_generator = partial_model_macro_generator(&input, &args, fields);

    // Charybdis::BaseModel types
    let primary_key_type = primary_key_type(fields);
    let partition_key_type = partition_key_type(fields);

    // Charybdis::BaseModel consts
    let db_model_name_const = db_model_name_const(&args);
    let find_by_primary_key_query_const = find_by_primary_key_query_const(&args, fields);
    let find_by_partition_key_query_consts = find_by_partition_key_query_consts(&args, fields);
    let find_first_by_partition_key_query_const = find_first_by_partition_key_query_const(&args, fields);
    let insert_query_const = insert_query_const(&args, fields);

    // Charybdis::Model consts
    let insert_if_not_exists_query_const = insert_if_not_exists_query_const(&args, fields);
    let update_query_const = update_query_const(&args, fields);
    let delete_query_const = delete_query_const(&args, fields);
    let delete_by_partition_key_query_const = delete_by_partition_key_query_const(&args, fields);

    // Charybdis::BaseModel methods
    let primary_key_values_method = primary_key_values_method(fields);
    let partition_key_values_method = partition_key_values_method(fields);

    // Collection consts
    let push_to_collection_consts = push_to_collection_consts(&args, fields);
    let push_to_collection_consts_if_exists = push_to_collection_consts_if_exists(&args, fields);
    let pull_from_collection_consts = pull_from_collection_consts(&args, fields);
    let pull_from_collection_consts_if_exists = pull_from_collection_consts_if_exists(&args, fields);

    // Collection methods
    let push_to_collection_methods = push_to_collection_methods(fields);
    let push_to_collection_methods_if_exists = push_to_collection_methods_if_exists(fields);
    let pull_from_collection_methods = pull_from_collection_methods(fields);
    let pull_from_collection_methods_if_exists = pull_from_collection_methods_if_exists(fields);

    // Counter methods
    let increment_counter_methods = increment_counter_methods(&args, fields);
    let decrement_counter_methods = decrement_counter_methods(&args, fields);

    // FromRow trait
    let from_row = from_row(struct_name, fields);

    // Current model macro rules
    let find_model_query_rule = find_model_query_rule(struct_name, &args, fields);
    let find_model_rule = find_model_rule(struct_name, &args, fields);
    let find_first_model_rule = find_first_model_rule(struct_name, &args, fields);
    let update_model_query_rule = update_model_query_rule(struct_name, &args, fields);
    let delete_model_query_rule = delete_model_query_rule(struct_name, &args);
    let delete_model_rule = delete_model_rule(struct_name, &args);

    // Associated functions
    let find_by_key_funs = find_by_primary_keys_functions(struct_name, &args, fields);
    let find_by_local_secondary_index_funs = find_by_local_secondary_index(struct_name, &args, fields);
    let find_by_global_secondary_index_funs = find_by_global_secondary_index(struct_name, &args, fields);
    let delete_by_cks_funs = delete_by_primary_key_functions(&args, fields);

    CharybdisFields::proxy_charybdis_attrs_to_scylla(&mut input);
    CharybdisFields::strip_charybdis_attributes(&mut input);

    let expanded = quote! {
        #[derive(charybdis::macros::scylla::SerializeRow)]
        #input

        impl #struct_name {
            #find_by_key_funs
            #delete_by_cks_funs

            #find_by_local_secondary_index_funs
            #find_by_global_secondary_index_funs

            #push_to_collection_consts
            #push_to_collection_consts_if_exists

            #pull_from_collection_consts
            #pull_from_collection_consts_if_exists

            // methods
            #push_to_collection_methods
            #push_to_collection_methods_if_exists

            #pull_from_collection_methods
            #pull_from_collection_methods_if_exists

            #increment_counter_methods
            #decrement_counter_methods
        }

       impl charybdis::model::BaseModel for #struct_name {
            // types
            #primary_key_type
            #partition_key_type

            // consts
            #db_model_name_const
            #find_by_primary_key_query_const
            #find_by_partition_key_query_consts
            #find_first_by_partition_key_query_const

            // methods
            #primary_key_values_method
            #partition_key_values_method
        }

        impl charybdis::model::Model for #struct_name {
            // operation consts
            #insert_query_const
            #insert_if_not_exists_query_const
            #update_query_const
            #delete_query_const
            #delete_by_partition_key_query_const

        }

        impl charybdis::scylla::FromRow for #struct_name {
            #from_row
        }

        #find_model_query_rule
        #find_model_rule
        #find_first_model_rule
        #update_model_query_rule
        #partial_model_generator
        #delete_model_query_rule
        #delete_model_rule
    };

    TokenStream::from(expanded)
}

#[proc_macro_attribute]
pub fn charybdis_view_model(args: TokenStream, input: TokenStream) -> TokenStream {
    let args: CharybdisMacroArgs = parse_macro_input!(args);
    let mut input: DeriveInput = parse_macro_input!(input);
    let mut new_fields = CharybdisFields::from_input(&input, &args);
    let fields = new_fields.populate(&args);

    let struct_name = &input.ident.clone();

    // Charybdis::BaseModel types
    let primary_key_type = primary_key_type(fields);
    let partition_key_type = partition_key_type(fields);

    // Charybdis::MaterializedView consts
    let db_model_name_const = db_model_name_const(&args);
    let find_by_primary_key_query_const = find_by_primary_key_query_const(&args, fields);
    let find_by_partition_key_query_consts = find_by_partition_key_query_consts(&args, fields);
    let find_first_by_partition_key_query_const = find_first_by_partition_key_query_const(&args, fields);

    // Charybdis::BaseModel methods
    let primary_key_values_method = primary_key_values_method(fields);
    let partition_key_values_method = partition_key_values_method(fields);

    // Current model rules
    let find_model_query_rule = find_model_query_rule(struct_name, &args, fields);

    // Associated functions
    let find_by_key_funs = find_by_primary_keys_functions(struct_name, &args, fields);

    CharybdisFields::proxy_charybdis_attrs_to_scylla(&mut input);
    CharybdisFields::strip_charybdis_attributes(&mut input);

    let expanded = quote! {
        #[derive(charybdis::macros::scylla::SerializeRow)]
        #[derive(charybdis::macros::scylla::FromRow)]
        #input

        impl #struct_name {
            #find_by_key_funs
        }

        impl charybdis::model::BaseModel for #struct_name {
            // types
            #primary_key_type
            #partition_key_type

            // consts
            #db_model_name_const
            #find_by_primary_key_query_const
            #find_by_partition_key_query_consts
            #find_first_by_partition_key_query_const

            // methods
            #primary_key_values_method
            #partition_key_values_method
        }

        impl charybdis::model::MaterializedView for #struct_name {}

        #find_model_query_rule
    };

    TokenStream::from(expanded)
}

#[proc_macro_attribute]
pub fn charybdis_udt_model(_: TokenStream, input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let gen = quote! {
        #[derive(charybdis::macros::scylla::FromUserType, charybdis::macros::scylla::SerializeValue)]
        #input
    };

    gen.into()
}

#[proc_macro_attribute]
pub fn char_model_field_attrs_gen(args: TokenStream, input: TokenStream) -> TokenStream {
    let args: CharybdisMacroArgs = parse_macro_input!(args);
    let input: DeriveInput = parse_macro_input!(input);

    let tkn_2 = char_model_field_attrs_macro_gen(args, input);

    tkn_2.into()
}