struct_reflection_derive/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, Data, DeriveInput, Fields};
6
7#[proc_macro_derive(StructReflection)]
8pub fn struct_reflection(input: TokenStream) -> TokenStream {
9    let input = parse_macro_input!(input as DeriveInput);
10
11    let struct_name = input.ident;
12    let generics = input.generics;
13    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
14
15    let generic_types: Vec<_> = generics.type_params().collect();
16
17    let field_list_code = match input.data {
18        Data::Struct(data_struct) => match data_struct.fields {
19            Fields::Named(fields) => fields
20                .named
21                .iter()
22                .map(|field| generate_field_code(field, &generic_types))
23                .collect(),
24            Fields::Unnamed(fields) => fields
25                .unnamed
26                .iter()
27                .enumerate()
28                .map(|(i, field)| generate_unnamed_field_code(i, field, &generic_types))
29                .collect(),
30            Fields::Unit => vec![],
31        },
32        _ => panic!("StructReflection can only be used on structs."),
33    };
34
35    let expanded = quote! {
36        impl #impl_generics StructReflectionHelper for #struct_name #ty_generics #where_clause {
37            fn struct_reflection() -> Option<Vec<String>> {
38                let mut fields = Vec::new();
39                #(#field_list_code)*
40                Some(fields)
41            }
42        }
43    };
44
45    TokenStream::from(expanded)
46}
47
48fn generate_field_code(
49    field: &syn::Field,
50    generic_types: &[&syn::TypeParam],
51) -> proc_macro2::TokenStream {
52    let field_name = field.ident.as_ref().unwrap().to_string();
53    let field_type = &field.ty;
54
55    if is_primitive_type(field_type) {
56        return quote! {
57            fields.push(#field_name.to_string());
58        };
59    }
60
61    for generic_type in generic_types {
62        if is_generic_parameter(field_type, generic_type) {
63            return quote! {
64                fields.push(#field_name.to_string());
65            };
66        }
67    }
68
69    if let Some(token_stream) = handle_tuple_type(field_type, &field_name, generic_types) {
70        return token_stream;
71    }
72
73    if let Some(token_stream) = handle_array_type(field_type, &field_name, generic_types) {
74        return token_stream;
75    }
76
77    quote! {
78        if let Some(inner_fields) = <#field_type as StructReflectionHelper>::struct_reflection() {
79            for inner_field in inner_fields {
80                fields.push(format!("{}__{}",  #field_name, inner_field));
81            }
82        } else {
83            fields.push(#field_name.to_string());
84        }
85    }
86}
87
88fn generate_unnamed_field_code(
89    index: usize,
90    field: &syn::Field,
91    generic_types: &[&syn::TypeParam],
92) -> proc_macro2::TokenStream {
93    let field_type = &field.ty;
94    let index_str = index.to_string();
95
96    if is_primitive_type(field_type) {
97        return quote! {
98            fields.push(#index_str.to_string());
99        };
100    }
101
102    for generic_type in generic_types {
103        if is_generic_parameter(field_type, generic_type) {
104            return quote! {
105                fields.push(#index_str.to_string());
106            };
107        }
108    }
109
110    if let Some(token_stream) = handle_tuple_type(field_type, &index_str, generic_types) {
111        return token_stream;
112    }
113
114    if let Some(token_stream) = handle_array_type(field_type, &index_str, generic_types) {
115        return token_stream;
116    }
117
118    quote! {
119        if let Some(inner_fields) = <#field_type as StructReflectionHelper>::struct_reflection() {
120            for inner_field in inner_fields {
121                fields.push(format!("{}__{}", #index, inner_field));
122            }
123        } else {
124            fields.push(#index.to_string());
125        }
126    }
127}
128
129// Helper functions for type checking
130fn is_primitive_type(ty: &syn::Type) -> bool {
131    if let syn::Type::Path(type_path) = ty {
132        if let Some(segment) = type_path.path.segments.last() {
133            let type_name = &segment.ident.to_string();
134            return matches!(
135                type_name.as_str(),
136                "bool"
137                    | "char"
138                    | "u8"
139                    | "u16"
140                    | "u32"
141                    | "u64"
142                    | "u128"
143                    | "usize"
144                    | "i8"
145                    | "i16"
146                    | "i32"
147                    | "i64"
148                    | "i128"
149                    | "isize"
150                    | "f32"
151                    | "f64"
152                    | "str"
153                    | "String"
154            );
155        }
156    }
157    false
158}
159
160fn is_generic_parameter(ty: &syn::Type, generic_type: &syn::TypeParam) -> bool {
161    if let syn::Type::Path(type_path) = ty {
162        return type_path.path.is_ident(&generic_type.ident);
163    }
164    false
165}
166
167fn is_tuple_type(ty: &syn::Type) -> bool {
168    if let syn::Type::Tuple(_) = ty {
169        return true;
170    }
171    false
172}
173
174fn handle_tuple_type(
175    ty: &syn::Type,
176    field_name: &str,
177    generic_types: &[&syn::TypeParam],
178) -> Option<proc_macro2::TokenStream> {
179    if let syn::Type::Tuple(tuple_type) = ty {
180        let mut element_handling = Vec::new();
181
182        // Generate code for each element of the tuple
183        for (i, elem) in tuple_type.elems.iter().enumerate() {
184            let index = proc_macro2::Literal::usize_unsuffixed(i);
185
186            // Check if element is a generic parameter
187            let mut is_generic = false;
188            for generic_type in generic_types {
189                if is_generic_parameter(elem, generic_type) {
190                    is_generic = true;
191                    element_handling.push(quote! {
192                        fields.push(format!("{}__{}",  #field_name, #index));
193                    });
194                    break;
195                }
196            }
197
198            // If not a generic parameter, try to handle recursively
199            if !is_generic {
200                element_handling.push(quote! {
201                    if let Some(inner_fields) = <#elem as StructReflectionHelper>::struct_reflection() {
202                        for inner_field in inner_fields {
203                            fields.push(format!("{}__{}__{}",  #field_name, #index, inner_field));
204                        }
205                    } else {
206                        fields.push(format!("{}__{}",  #field_name, #index));
207                    }
208                });
209            }
210        }
211
212        return Some(quote! {
213            #(#element_handling)*
214        });
215    }
216    None
217}
218
219// CORRECTED FUNCTION: Handle array types properly
220fn handle_array_type(
221    ty: &syn::Type,
222    field_name: &str,
223    generic_types: &[&syn::TypeParam],
224) -> Option<proc_macro2::TokenStream> {
225    if let syn::Type::Array(array_type) = ty {
226        let elem_type = &*array_type.elem;
227        let array_len = &array_type.len;
228
229        // Case 1: Array of tuples like [(T, U); N]
230        if is_tuple_type(elem_type) {
231            if let syn::Type::Tuple(tuple_type) = elem_type {
232                let tuple_size = tuple_type.elems.len();
233
234                return Some(quote! {
235                    for i in 0..#array_len {
236                        for j in 0..#tuple_size {
237                            fields.push(format!("{}__{}__{}",  #field_name, i, j));
238                        }
239                    }
240                });
241            }
242        }
243
244        // Case 2: Nested arrays with tuples like [[(T, U); M]; N]
245        if let syn::Type::Array(inner_array_type) = elem_type {
246            let inner_elem_type = &*inner_array_type.elem;
247            let inner_array_len = &inner_array_type.len;
248
249            if is_tuple_type(inner_elem_type) {
250                if let syn::Type::Tuple(tuple_type) = inner_elem_type {
251                    let tuple_size = tuple_type.elems.len();
252
253                    return Some(quote! {
254                        for i in 0..#array_len {
255                            for j in 0..#inner_array_len {
256                                for k in 0..#tuple_size {
257                                    fields.push(format!("{}__{}__{}__{}", #field_name, i, j, k));
258                                }
259                            }
260                        }
261                    });
262                }
263            }
264        }
265
266        // Case 3: Check if array element is primitive type
267        if is_primitive_type(elem_type) {
268            return Some(quote! {
269                for i in 0..#array_len {
270                    fields.push(format!("{}__{}", #field_name, i));
271                }
272            });
273        }
274
275        // Case 4: Array of generic type [T; N]
276        for generic_type in generic_types {
277            if is_generic_parameter(elem_type, generic_type) {
278                return Some(quote! {
279                    for i in 0..#array_len {
280                        fields.push(format!("{}__{}", #field_name, i));
281                    }
282                });
283            }
284        }
285
286        // Case 5: Nested array [[ElementType; M]; N]
287        if let syn::Type::Array(inner_array_type) = elem_type {
288            let inner_elem_type = &*inner_array_type.elem;
289            let inner_array_len = &inner_array_type.len;
290
291            // Case 5.1: If inner element is primitive
292            if is_primitive_type(inner_elem_type) {
293                return Some(quote! {
294                    for i in 0..#array_len {
295                        for j in 0..#inner_array_len {
296                            fields.push(format!("{}__{}__{}", #field_name, i, j));
297                        }
298                    }
299                });
300            }
301
302            // Case 5.2: If inner element is generic
303            for generic_type in generic_types {
304                if is_generic_parameter(inner_elem_type, generic_type) {
305                    return Some(quote! {
306                        for i in 0..#array_len {
307                            for j in 0..#inner_array_len {
308                                fields.push(format!("{}__{}__{}", #field_name, i, j));
309                            }
310                        }
311                    });
312                }
313            }
314        }
315
316        // Default case for arrays: Try to get nested fields or use basic indexing
317        return Some(quote! {
318            if let Some(sub_fields) = <#elem_type as StructReflectionHelper>::struct_reflection() {
319                for i in 0..#array_len {
320                    for sub_field in &sub_fields {
321                        fields.push(format!("{}__{}__{}", #field_name, i, sub_field));
322                    }
323                }
324            } else {
325                for i in 0..#array_len {
326                    fields.push(format!("{}__{}", #field_name, i));
327                }
328            }
329        });
330    }
331    None
332}